The Issue
I'm animating some text in from left to right. It should only run once and be done when it's in view (should not run until it's in view!), but for some reason, while it's correctly waiting for it to be in view, this is looping over and over, slowly pushing the element off the page.
If you have any insight for me regarding how to stop this oddity, I would greatly appreciate it!
Here's an example of the website where this can be seen as well if find it useful.
https://stable.stable-demos.com/who-we-are/
jQuery(function($){
$(window).scroll(function () {
var y = $(window).scrollTop(),
x = $('.div6').offset().top - 100;
if (y > x) {
$('.div6').delay(1300).animate({ opacity: 1, "margin-left": '+=50'});
} else {
// do not run the animation
}
});
});
.div6 {
opacity: 0;
font-size: 48px;
margin-left: -50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div6">
<div class="wrapper">
<h2>At The Stable, we are for you. We tackle your problems for you and celebrate your victories with you.</h2>
</div>
</div>
You need to keep a track of whether the animation has been triggered, and not let it fire after that. It can be done through a variable as shown below:
var done = false;
jQuery(function($) {
$(window).scroll(function() {
if (!done) {
var y = $(window).scrollTop(),
x = $('.div6').offset().top - 100;
if (y > x) {
done = true;
$('.div6')
.delay(1300)
.animate({
opacity: 1,
marginLeft: '+=50'
});
}
}
});
});
.div6 {
opacity: 0;
font-size: 48px;
margin-left: -50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="div6">
<div class="wrapper">
<h2>At The Stable, we are for you. We tackle your problems for you and celebrate your victories with you.</h2>
</div>
</div>
Related
Is there a way to create an multiple row image slider like the one in the image below using just css? or is there a way to do this with angular?
The slider needs to move as one (single rows cannot be swiped individually).
First you need to understand the overflow property in css:
https://css-tricks.com/almanac/properties/o/overflow/
This will allow you to see there is a scroll property. That can make your scroll bars. Yours should use overflow-x to scroll the direction you want it to go.
As for angular, you need to look into ng-repeat command. Here is a fiddle that is doing what you are looking for:
<div ng-repeat="user in users | limitTo:display_limit">
http://jsfiddle.net/bmleite/hp4w7/
Quick answer to your question.. no, there is no way to do this with just CSS because you will have to handle the swipe, touch, click, etc. events using javascript. I guess I was working under the assumption that you would be adding angularjs into your application solely for this purpose, so I made a jQuery solution. If that is a wrong assumption, I will rewrite an angular solution.
Basically, the idea is that you structure your HTML/CSS in a way to get the effect of the sliding within a given container, and then use event handlers to update the slider as the user interacts with it.
Working DEMO
HTML
<div class="slider-display centered">
<div class="image-container">
<div class="image">Image<br>1</div>
<div class="image">Image<br>2</div>
<div class="image">Image<br>3</div>
<div class="image">Image<br>4</div>
<div class="image">Image<br>5</div>
<div class="image">Image<br>6</div>
<div class="image">Image<br>7</div>
<div class="image">Image<br>8</div>
<div class="image">Image<br>9</div>
<div class="image">Image<br>10</div>
<div class="image">Image<br>11</div>
<div class="image">Image<br>12</div>
<div class="image">Image<br>13</div>
<div class="image">Image<br>14</div>
<div class="image">Image<br>15</div>
<div class="image">Image<br>16</div>
<div class="image">Image<br>17</div>
<div class="image">Image<br>18</div>
</div>
</div>
<div class="centered" style="text-align: center; max-width: 350px;">
<button class="move-left"><--</button>
<button class="move-right">--></button>
</div>
Javascript
$(function () {
var getWidth = function ($element) {
var total = 0;
total += $element.width();
total += Number($element.css("padding-left").replace("px", ""));
total += Number($element.css("padding-right").replace("px", ""));
total += Number($element.css("border-left").split("px")[0]);
total += Number($element.css("border-right").split("px")[0]);
total += Number($element.css("margin-left").split("px")[0]);
total += Number($element.css("margin-right").split("px")[0]);
return total;
};
var sliderPosition = 0;
var imageWidth = getWidth($(".image").eq(0));
$(".move-left").on("click.slider", function () {
var maxVisibleItems = Math.ceil($(".slider-display").width() / imageWidth);
var maxItemsPerRow = Math.ceil($(".image-container").width() / imageWidth);
var numRows = Math.ceil($(".image-container .image").length / maxItemsPerRow);
var maxPosition = numRows > 1 ? maxVisibleItems - maxItemsPerRow : maxVisibleItems - $(".image-container .image").length;
if (sliderPosition > (maxPosition)) {
sliderPosition--;
var $imageContainer = $(".image-container");
$(".image-container").animate({
"margin-left": sliderPosition * imageWidth
},{
duration: 200,
easing: "linear",
queue: true,
start: function () {
$(".move-left").prop("disabled", true);
},
done: function () {
$(".move-left").prop("disabled", false);
}
});
}
});
$(".move-right").on("click.slider", function () {
if (sliderPosition < 0) {
sliderPosition++;
var $imageContainer = $(".image-container");
$(".image-container").animate({
"margin-left": sliderPosition * imageWidth
},{
duration: 200,
easing: "linear",
queue: true,
start: function () {
$(".move-right").prop("disabled", true);
},
done: function () {
$(".move-right").prop("disabled", false);
}
});
}
});
});
CSS
.image {
float: left;
height: 80px;
width: 80px;
background: #888888;
text-align: center;
padding: 5px;
margin: 5px;
font-size: 1.5rem;
}
.image-container {
width: 650px;
position: relative;
}
.slider-display {
max-width: 450px;
overflow: hidden;
background: #ddd
}
.centered {
margin: 0 auto;
}
index.html:
<script src="app.js"></script>
<div ng-app="app" ng-controller="app" ng-style="{ color: thecolor }">
foo
</div>
app.js:
angular.module("app", [])
.controller("app", function() {
$scope.thecolor = "red";
});
Fiddle.
Expected: "foo" is rendered red. Observed: "foo" is rendered black (the default).
So basically I'm trying to set the style of an element from variables in the scope. I've seen this working in Angular 1.0.2, but I need it to work in 1.4.8.
EDIT: Once I've updated the controller to include the $scope dependency, I still can't get certain things to work, such as positioning within a parent element.
index.html:
<link rel="stylesheet" href="app.css">
<script src="app.js"></script>
<div ng-app="app" ng-controller="app">
<div ng-style="{ left: x, top: y }">
foo
</div>
</div>
app.js:
angular.module("app", [])
.controller("app", ["$scope", function($scope) {
$scope.x = 100;
$scope.y = 100;
}]);
app.css:
body {
margin: 0px;
}
div {
position: relative;
width: 100vw;
height: 100vh;
}
div div {
position: absolute;
}
Expected: "foo" is rendered 100px down and to the right. Observed: no displacement.
Fiddle.
Controller in your code doesnt have $scope as Dependency Injection (DI), based on your updated question modifying the answer as below
controller
angular.module("app", [])
.controller("app", ['$scope',function($scope) {
$scope.thecolor = "red";
$scope.x = 100;
$scope.y = 100;
}]);
view
<div ng-style="{ left: x + 'px', top: y + 'px' }">foo</div>
updated fiddle
You have to specify the unit for the CSS values - you can do it in the view:
<div ng-style="{ left: x + 'px', top: y + 'px' }">
You missed $scope injection in controller.
angular.module("app", [])
.controller("Ctrl", function($scope) {
$scope.thecolor = "red";
});
Here is a working demo:
http://jsbin.com/jomacoj/edit?html,js,console,output
I have made a simple swap animation with ng-animate-swap directive, nothing fancy. It works well in forward direction, but fails animating backwards correctly.
The problem is, that the entering slide will not be visible before animation ends.
Check an example in Plunker.
Here is the controller code:
var elem = document.querySelector('.wrap');
var $elem = angular.element(elem);
var slides = [
{ color: "#f00" },
{ color: "#0f0" },
{ color: "#00f" },
];
var current = 0;
$scope.slide = slides[ current ];
// switch to next slide
$scope.nextSlide = function(){
$elem.removeClass('animate-back');
if(slides.length <= ++current){
current = 0;
}
$scope.slide = slides[ current ];
};
// switch to prev slide
$scope.prevSlide = function(){
$elem.addClass('animate-back');
if(--current<0){
current = slides.length-1;
}
$scope.slide = slides[ current ];
};
the HTML:
<div class="wrap" ng-controller="AppCtrl">
<div class="container">
<div ng-animate-swap="slide" class="slide" ng-style="{background:slide.color}"></div>
</div>
<button ng-click="prevSlide()">Previous Slide</button>
<button ng-click="nextSlide()">Next Slide</button>
</div>
the CSS:
.slide.ng-enter-active,
.slide.ng-leave {
transform: translate(0,0);
}
// forward
.slide.ng-enter {
transform: translate(0,-100%);
}
.slide.ng-leave-active {
transform: translate(0,100%);
}
// backward
.animate-back .slide.ng-enter {
transform: translate(0,100%);
}
.animate-back .slide.ng-leave-active {
transform: translate(0,-100%);
}
I think it's simple CSS issue, but can not wrap my head around it.
What am I missing here?
You're right! The problem was the css. The direction was missing for the incoming slide.
".animate-back .slide.ng-enter-active"
This should work.
.animate-back .slide.ng-enter {
transform: translate(0,100%);
}
.animate-back .slide.ng-enter-active {
transform: translate(0,0);
}
.animate-back .slide.ng-leave-active {
transform: translate(0,-100%);
}
Updated Plunker: http://plnkr.co/edit/Uze8e8DmmjlaSqEeBELe?p=preview
I am trying to implement correctly scaling and zooming in css way. I created an example with scaled view. When click, the view should be zoomed and then to be able to scroll.
https://jsfiddle.net/opb5tcy8/4/
I have several issues with it:
Can I somehow get rid of the margin-left and margin-top on the .zoomed class? I did not manage to scale it without necessity to shift it with these margins.
When clicked, I can get the click position by clientX. I would like to use it to fluently scroll to the clicked position during zooming. However I can't manage the scroll to be fluent and when removing the margin-left it is kind of jumpy and not nice.
When you zoom in and move the scroll to the center and then zoom out, you can see the zoom is not nice as it first scrolls to the right. Is there a way to prevent it?
When you scroll to corners in Chrome on OSX it tends do navigate back/forward in browser. Is there a way to prevent this behaviour?
UPDATE:
The first part can be solved with transform-origin: 0 0. The other issues stays mostly the same as it is demonstrated.
Hm... I could say it is impossible to satisfy point 2 your condition with current browsers' support. The other are possible, as in this demo:
$(document).ready(function() {
var windowHalfWidth = $("#window").width() / 2;
var scalingFactor = 0.55;
var throtte = false;
$("#slider").click(function(event) {
//Simple event throtte to prevent click spamming breaking stuff up
if (throtte) return false;
throtte = true;
setTimeout(function() {
throtte = false;
}, 1000);
var xSelf = event.pageX - $("#window").offset().left + $("#window").scrollLeft();
if ($(this).hasClass("zoomed")) {
$("#window").animate({
scrollLeft: (xSelf / scalingFactor - windowHalfWidth)
}, 1000, "linear");
} else {
$("#window").animate({
scrollLeft: (xSelf * scalingFactor - windowHalfWidth)
}, 1000, "linear");
}
$("#slider").toggleClass("zoomed");
});
});
body {
background-color: #eee;
margin-top: 10px; /*reduced margin for easier view in SO */
}
#window {
width: 500px;
height: 200px;
margin: 0 auto;
overflow-x: auto;
overflow-y: hidden;
border: 1px solid #999;
position: relative;
background-color: white;
}
#slider {
width: 900px;
height: 600px;
background-color: #fff;
position: absolute;
transition: 1s linear;
top: 0;
left: 0;
transform-origin: 0 0;
}
#slider.zoomed {
transform: scale(0.55);
}
#slider div {
width: 50px;
height: 50px;
line-height: 50px;
position: absolute;
top: 75px;
background-color: #eee;
text-align: center;
}
#obj1 {
left: 10px;
}
#obj2 {
left: 210px;
}
#obj3 {
left: 410px;
}
#obj4 {
left: 610px;
}
#obj5 {
left: 810px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="window">
<div id="slider" class="zoomed">
<div id="obj1">1</div>
<div id="obj2">2</div>
<div id="obj3">3</div>
<div id="obj4">4</div>
<div id="obj5">5</div>
</div>
</div>
As you can see, the zooming & scrolling is quite laggy, especially when the far right size is zoomed in.
The reason is simple, because jQuery and css both have their own animation loop, and they are not in sync. In order to solve this we'll need to somehow manage to do both scrolling & scaling animations with only one system, either jQuery or CSS.
Problem is: jQuery don't have a scaling feature, and css can't scroll elements. Wonderful.
If your scaling can be done with width/height though, it would be possible, using jquery width&height animate(). But if the #slider consists of many components I guess it can't be done.
So um writing an answer just to say it's impossible is kind of a let down, so I think maybe I can suggest an alternative, using dragging to scroll content (similar to the way Google map work):
var windowHalfWidth, startX, startLeft, minLeft, dragging = false,
zooming = false;
var zoomElement = function(event) {
var xSelf = event.pageX - $("#window").offset().left - parseFloat($("#slider").css("left"));
if ($("#slider").hasClass("zoomed")) {
minLeft = windowHalfWidth * 2 - 900;
var newLeft = Math.min(Math.max((-(xSelf / 0.55 - windowHalfWidth)), minLeft), 0);
$("#slider").css("left", newLeft + "px");
} else {
minLeft = windowHalfWidth * 2 - 900 * 0.55;
var newLeft = Math.min(Math.max((-(xSelf * 0.55 - windowHalfWidth)), minLeft), 0);
$("#slider").css("left", newLeft + "px");
}
$("#slider").toggleClass("zoomed");
}
$(document).ready(function() {
windowHalfWidth = $("#window").width() / 2;
minLeft = windowHalfWidth * 2 - 900 * 0.55;
$("#slider").on({
mousedown: function(event) {
dragging = true;
startX = event.pageX;
startLeft = parseFloat($(this).css("left"));
},
mousemove: function(event) {
if (dragging && !zooming) {
var newLeft = Math.min(Math.max((startLeft + event.pageX - startX), minLeft), 0);
$("#slider").css("left", newLeft + "px");
}
},
mouseup: function(event) {
dragging = false;
if (Math.abs(startX - event.pageX) < 30 && !zooming) {
// Simple event throtte to prevent click spamming
zooming = true;
$("#slider").css("transition", "1s");
setTimeout(function() {
zooming = false;
$("#slider").css("transition", "initial");
}, 1000);
zoomElement(event);
}
},
mouseleave: function() {
dragging = false;
}
});
});
body {
background-color: #eee;
margin-top: 10px; /*reduced margin for easier view in SO */
}
#window {
width: 500px;
height: 200px;
margin: 0 auto;
overflow: hidden;
border: 1px solid #999;
position: relative;
background-color: white;
}
#slider {
width: 900px;
height: 600px;
background-color: #fff;
position: absolute;
top: 0;
left: 0;
transform-origin: 0 0;
}
#slider.zoomed {
transform: scale(0.55);
}
#slider div {
width: 50px;
height: 50px;
line-height: 50px;
position: absolute;
top: 75px;
background-color: #eee;
text-align: center;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
#obj1 {
left: 10px;
}
#obj2 {
left: 210px;
}
#obj3 {
left: 410px;
}
#obj4 {
left: 610px;
}
#obj5 {
left: 810px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="window">
<div id="slider" class="zoomed">
<div id="obj1">1</div>
<div id="obj2">2</div>
<div id="obj3">3</div>
<div id="obj4">4</div>
<div id="obj5">5</div>
</div>
</div>
This variation manages to get CSS to do both animation, by sacrificing the scrollbar (which is pretty ugly imo, who needs it?) and use css left instead.
So I hope if in the end you can't find a good solution, at least you have this to consider as fall back version.
I'll address the points individually and then give an example at the end.
When clicked, I can get the click position by clientX. I would like to
use it to fluently scroll to the clicked position during zooming.
In my opinion scroll animations during transitions can be a bit choppy in webkit browsers. Try balancing the animation time of the jQuery effect with the animation time of the css transition.
When you zoom in and move the scroll to the centre and then zoom out, you can see the zoom is not nice as it first scrolls to the right. Is there a way to prevent it?
Bring the scrollLeft property of the div#window back to 0px. Again, tweaking the animation times will make this less jerky.
When you scroll to corners in Chrome on OSX it tends do navigate back/forward in browser. Is there a way to prevent this behaviour?
You could use the mouseover and mouseout events to toggle a overflow:hidden css on the body.
Here's an example change to your code:
var slider = $("#slider").on('click', function(event) {
if (!slider.hasClass('zoomed')) {
// zoom back to left position
$('#window').animate({scrollLeft:'0px'});
}else{
// zoom to click position within slider
$('#window').animate({scrollLeft:event.clientX + 'px'}, 2000);
}
slider.toggleClass("zoomed");
});
/* stop window scrolling when using slider */
slider
.on('mouseover', function () {
$(document.body).css({overflow:'hidden'});
})
.on('mouseout', function () {
$(document.body).css({overflow:'auto'});
});
And an updated fiddle.
I have a div tag that looks as follows:
<div id="loadingDiv" class="loadingDiv"; style="position:absolute; left:400px; top:292px;">
<strong>Retrieving Data - One Moment Please...</strong>
</div>
It seems that Chrome and IE do not render this the same way. In IE, the text is much further to the left than with Chrome. I don't know why this is. So, is there a way I can create a style that is dependent on the browser type? For example, if the browser is IE, I'd like the left value to be maybe 300px, and 400px if Chrome. Or, is there a better way to handle this?
Even if I don't recommend to use browser specific CSS, it is always much better to optimize your CSS to look at least simmilar in all browsers, you can do what you want by using of some javascript combined with CSS.
Here is the code:
<html>
<head>
<title>browser specific css</title>
<style>
.loadingDiv {
position: absolute;
display: none;
font-weight: bold;
}
.loadingDiv.ie {
display: block;
left: 300px;
top: 292px;
background: #00CCFF;
color: #454545;
}
.loadingDiv.chrome {
display: block;
left: 400px;
top: 292px;
background: #FCD209;
color: #E53731;
}
.loadingDiv.firefox {
display: block;
left: 400px;
top: 292px;
background: #D04F16;
color: #FFFFFF;
}
.loadingDiv.default {
display: block;
left: 400px;
top: 292px;
}
</style>
<script>
window.onload = function() {
var check_for_ie = detect_browser("MSIE");
var check_for_chrome = detect_browser("Chrome");
var check_for_firefox = detect_browser("Firefox");
var browser_name = "";
var loading_div = document.getElementById("loadingDiv");
var loading_div_html = loading_div.innerHTML;
if (check_for_ie == true) {
browser_name = "Internet Explorer";
loading_div.className = "loadingDiv ie";
}
else if (check_for_chrome == true) {
browser_name = "Google Chrome";
loading_div.setAttribute("class","loadingDiv chrome");
}
else if (check_for_firefox == true) {
browser_name = "Firefox";
loading_div.setAttribute("class","loadingDiv firefox");
}
else {
browser_name = "Unchecked browser";
loading_div.setAttribute("class","loadingDiv default");
}
loading_div.innerHTML = loading_div_html + "(you are browsing with "+browser_name+")";
}
function detect_browser(look_for) {
var user_agent_string = navigator.userAgent;
var search_for_string = user_agent_string.search(look_for);
if (search_for_string > -1) {
return true;
}
else {
return false;
}
}
</script>
</head>
<body>
<div id="loadingDiv" class="loadingDiv" >
Retrieving Data - One Moment Please...
</div>
</body>
</html>
And here is the working example:
http://simplestudio.rs/yard/browser_specific_css/browser_specific_css.html
EDIT:
If you need to check for some other browsers look at user agent string of that specific browser and find something that is unique in it and makes a difference between that browser and the others and use that like this:
var check_for_opera = detect_browser("Opera");
Detecting browsers by user agent could be tricky so be careful, even upgrade my function if you need...
NOTE, that this is just quick example...