How can I fix this timing issue in chrome? In firefox, the closing animation goes smoothly but in chrome the two elements are off. I cannot place the elements in one container to transition.
notice the white border between the off canvas and the button in the slide. They are a bit off.
$(document).ready(function () {
var OffCanvasWidget = {
options: {
id: 'OffCanvasWidget',
trigger: $('[data-offcanvas-trigger]'),
close: $('[data-offcanvas-close]')
},
init: function () {
this.addListeners();
},
addListeners: function () {
var that = this;
this.options.trigger.on('click', function (event) {
event.preventDefault();
that.toggleOffCanvas();
});
},
toggleOffCanvas: function () {
$('body').toggleClass('offcanvas-show');
}
};
OffCanvasWidget.init();
});
.trigger {
transition: transform .7s ease;
display: inline-block;
transform: rotate3d(0, 0, 1, -90deg) perspective(1px) scale(1.001);
position: fixed;
right: -22px;
top: 30vh;
}
.offcanvas-show .trigger {
transform: translateX(-218px) rotate3d(0, 0, 1, -90deg) perspective(1px) scale(1.001);
}
.offcanvas {
transform: translateX(100%);
position: fixed;
transition: transform .7s ease;
top: 0;
right: 0;
bottom: 0;
left: auto;
width: 200px;
}
.offcanvas-show .offcanvas {
transform: translateX(0);
}
.trigger, .offcanvas {
background-color: black;
color: white;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" class="trigger" data-offcanvas-trigger>Follow us</a>
<div class="offcanvas">
</div>
Why not just make the 'tab' element a little wider so it 'underlaps' the tray enough so as to not notice any gap :)
Related
I am trying to chain together 2 transforms, but I want the 2nd one to begin after the 1st one ends.
This is how I am trying to do it:
.trailingNote {
position:absolute;
bottom:0px;
background:black;
width:20px;
transition: transform 5s;
transition-timing-function: linear;
}
.trailingNote-moveUp{
transform: scaleY(10) translateY(-200px);
}
Basically, I want the element to be scaled by 10 on the y axis, then, after scaleY ends, start translateY(-200px) to move the scaled element up.
Link to CodePen: https://codepen.io/Sederfo/pen/abqOoOP
Use CSS keyframes
function startAnimation() {
var element = document.getElementById("x");
element.classList.add("trailingNote-moveUp");
}
.trailingNote {
position:absolute;
bottom:0px;
background:black;
width: 20px;
}
.trailingNote:hover, .trailingNote-moveUp {
animation: animate 5s linear forwards;
}
#keyframes animate {
0% {
transform: none;
}
50% {
transform: scaleY(10);
}
100% {
transform: scaleY(10) translateY(-200px);
}
}
<div id="x" class="trailingNote">Note</div>
<button onclick="startAnimation()">Animate</button>
You can use something like this.
const box = document.getElementById('box');
const button = document.querySelector('button');
button.addEventListener('click', () =>{
box.classList.add('transform-1');
box.addEventListener('transitionend', () =>{
setTimeout(function (){
box.classList.add('transform-2');
},1000)
})
});
*,
*::before,
*::after {
box-sizing: border-box;
}
body {
min-height: 100vh;
margin: 0;
background-color: bisque;
display: grid;
place-content: center;
}
button{
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
}
.box{
width: 5rem;
height: 5rem;
background-color: brown;
transition: all 1s linear;
}
.transform-1{
transform: scaleY(5);
}
.transform-2{
transform: translateY(-2000px);
}
<div class="box" id="box"></div>
<button>Click me</button>
I have a page transition I'm trying to work into my site. I have 2 50% height, 100% width elements, one placed before and after the body (with pseudo-selectors). I would like the 2 elements to slide to the middle of the screen, covering the background content. The transition is triggered when the "is-changing" class is added to the body, via Javascript.
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
transform: translateY(0);
}
.loading-bar {
position: fixed;
height: 2px;
width: 90%;
}
.loading-bar::before {
position: absolute;
background-color: aqua;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.is-changing .loading-bar::before {
transform: scaleX(1);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
It looks to me like you're running into two issues.
The first issue is that you forgot to include the content attribute in your pseudo elements (often this will be empty, like content: ""). Without this attribute, your pseudo elements will not exist in the DOM. Running your code snippet and inspecting it confirms this, since the pseudo elements are nowhere to be found.
Second, you're creating multiple pseudo elements. body::before is it's own pseudo element, and body.is-changing::before is a separate pseudo element. If you are hoping to create a constant set of elements that act as "doors" for a loading display, you may want to consider creating two real elements that sit in position: fixed above and below the viewport, and then slide in or out when a class is added. Perhaps these could be div.upper-door and div.lower-door.
Also, it looks to me like you're in need of a transition for your transform, or else the pseudo elements will just "snap" back and forth. You can take control of the position of your elements at different points during this transition by using a css animation. Your JavaScript would largely remain the same, except for targeting the .upper-door and .lower-door divs using document.querySelector(), or simply using IDs rather than classes and targeting with getElementById(), if that makes more sense for you. Your css might look like this:
div.upper-door {
top: 0;
transform: translateY(-100%);
}
div.upper-door.is-changing {
animation-duration: 5s;
animation-name: upper-door-closeopen;
}
div.lower-door {
bottom: 0;
transform: translateY(100%);
}
div.lower-door.is-changing {
animation-duration: 5s;
animation-name: lower-door-closeopen;
}
#keyframes upper-door-closeopen {
0% {
transform: translateY(-100%);
}
25% {
transform: translateY(0);
}
75% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
#keyframes lower-door-closeopen {
0% {
transform: translateY(100%);
}
25% {
transform: translateY(0);
}
75% {
transform: translateY(0);
}
100% {
transform: translateY(100%);
}
}
The css animation will be triggered when .is-changing is added to the element. As you experiment, you may find different permutations of this solution (such as using event listeners if a button click is triggering the loading screen) to be ideal.
There is a great resource on MDN for css animations if you would like more information.
You missed to add the content property on the pseudo-elements which is mandatory to make them available on the page. You also missed to add the transition property on the pseudo-elements to achieve your animation of sliding up/down.
Here's a snippet containing a working demo, I only used the code that is related to your issue:
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
position: relative; /* not really related to your issue but, to make sure that the body's pseudo-elements are positioned relative to the body */
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
content: ""; /* make the pseudo-elements available */
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
transition: all .8s ease-out; /* allow the animation, change this rule per your requirements */
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
transform: translateY(0);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
Learn more about after pseudo-element.
Learn more about before pseudo-element.
You can use the following
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
content:'';
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
}
body::before {
content:'';
top: 0;
transform: translateY(-100%);
transition: .5s all;
}
body::after {
content:'';
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
content:'';
transform: translateY(0);
}
.loading-bar {
position: fixed;
height: 2px;
width: 90%;
}
.loading-bar::before {
content:'';
position: absolute;
background-color: aqua;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.is-changing,.loading-bar::before {
transform: scaleX(1);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
I want to add 2 transition transforms
But I want to start the second transform after the end of the first transform
the element should go to a point slowly and after that it should go to another point
transform: translate(0%, 300%), translate(15%, -136%);
You cannot do this with just a single element using transition because when you put more than one translate within the transform, the transform property on the whole is transitioned and not one by one.
With pure CSS transition using an extra wrapper element:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
.wrapper {
position: relative;
height: 100px;
width: 100px;
transition: all 1s 1s;
}
.content {
position: absolute;
height: 100%;
width: 100%;
border: 1px solid;
transition: all 1s;
}
body:hover .content {
transform: translate(15%, -136%);
transition: all 1s 1s;
}
body:hover > .wrapper {
transform: translate(0%, 300%);
transition: all 1s;
}
body {
min-height: 100vh;
}
<div class='wrapper'>
<div class='content'>Some text</div>
</div>
Transition with a bit of JS/jQuery without any extra elements:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
$(document).ready(function() {
var isHover; /* variable to track state */
$('body').hover(function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
}, function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
});
$('.content').on('transitionend', function() {
if (isHover) {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)');
} else {
$('.content').css('transform', 'none');
}
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
transition: all 1s;
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>
With animation and no extra element:
Using animations this can be done using a single element but the reverse effect is tough to achieve. We would have to write extra code for this and even then it will be complex.
.content {
position: relative;
height: 100px;
width: 100px;
border: 1px solid;
}
body:hover > .content {
animation: move 1s forwards;
}
#keyframes move {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
body {
min-height: 100vh;
}
<div class='content'>Some text</div>
Animations with reverse effect:
Below is a snippet which produces the reverse effect also using CSS animations. But as you can see it is a bit complex. We can do this using a single animation also but it would become more complex.
$(document).ready(function() {
$('body').hover(function() {
$('.content').css('transform', 'none');
$('.content').removeClass('hover-out').addClass('hover-in');
}, function() {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)'); /* as soon as an animation is removed, the element would snap back to original state, to avoid that we have to add final state via inline style */
$('.content').removeClass('hover-in').addClass('hover-out');
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
}
.hover-in {
animation: hover-in 1s forwards;
}
.hover-out {
animation: hover-out 1s forwards;
}
#keyframes hover-in {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
#keyframes hover-out {
0% {
transform: translate(0%, 300%) translate(15%, -136%);
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: none;
}
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>
I have list of items (li) that are stacked around in circle using transformation. Items have different angle depending on its initial position in the circle:
transform: rotate(0deg) translateY(-75px) rotate(-0deg)
transform: rotate(45deg) translateY(-75px) rotate(-45deg)
... etc
last negative rotate is to rotate ccw to keep element in up position.
translate Y is radius offset.
What I would like to accomplish is to rotate all items around for various number of degrees or let's say if one item gets clicked I would like all items to rotate so that clicked item will be on the top position (0deg).
What would be best way to do this? Looking for some clever ways.. All items should ofcourse rotate at the same time - like a dialer on the old phone.
With some jQuery, you can change the class of the container and assign the rotation value to the container :
fiddle
$('.one').click(function() {
$('#container').removeClass('second third fourth').addClass('first');
});
$('.two').click(function() {
$('#container').removeClass('first third fourth').addClass('second');
});
$('.three').click(function() {
$('#container').removeClass('first second fourth').addClass('third');
});
$('.four').click(function() {
$('#container').removeClass('first second third').addClass('fourth');
});
#container {
position: relative;
width: 30%;
padding-bottom: 30%;
margin: 10% auto;
transition: transform .5s ease-out;
}
.elt {
position: absolute;
padding: 2%;
background: teal;
cursor: pointer;
transition: transform .5s ease-out;
}
.one { top: 0; left: 47.5%; }
.two { top: 47.5%; right: 0; }
.three { bottom: 0; left: 47.5%; }
.four { top: 47.5%; left: 0; }
#container.first { transform: rotate(0deg); }
#container.first div { transform: rotate(0deg); }
#container.second { transform: rotate(-90deg); }
#container.second div { transform: rotate(90deg); }
#container.third { transform: rotate(180deg); }
#container.third div { transform: rotate(-180deg); }
#container.fourth { transform: rotate(90deg); }
#container.fourth div { transform: rotate(-90deg); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="elt one">1</div>
<div class="elt two">2</div>
<div class="elt three">3</div>
<div class="elt four">4</div>
</div>
I do have a running code sample (http://jsfiddle.net/Fuunq/) which shows a slide down animation using angular.js (1.2.0). But there are two issues that I don't know how to solve:
a) When clicking on the 'Add item' link, the animation first moves down the 'Add item' link and then slides in the new item from the top. How can this be changed, that the 'Add item' link slides down together with the new appearing item?
b) How can I prevent that the items do fade in when the page gets loaded for the first time?
HTML
<div ng-controller="MyCtrl">
<div ng-repeat="item in items" class="animation-slide-down">
<div class="item">
<div>Name: {{item.name}}</div>
<div>Color: {{item.color}}</div>
</div>
</div>
Add item
</div>
CSS
.item {
margin-top: 5px;
padding: 15px;
background-color: #34ac54;
border: 1px solid black;
}
#-webkit-keyframes slide_down {
0% {
-webkit-transform: translateY(-100%);
opacity: 0;
z-index: -100;
}
99% {
z-index: -100;
}
100% {
-webkit-transform: translateY(0);
opacity: 1;
z-index: 0;
}
}
#keyframes slide_down {
0% {
transform: translateY(-100%);
opacity: 0;
z-index: -100;
}
99% {
z-index: -100;
}
100% {
transform: translateY(0);
opacity: 1;
z-index: 0;
}
}
.animation-slide-down.ng-enter {
-webkit-animation: slide_down 3s ease-in;
animation: slide_down 4s ease-in;
}
JS
var myApp = angular.module('myApp', ['ngAnimate']);
function MyCtrl($scope) {
$scope.name = 'foo';
$scope.items = [
{name: 'Item 1', color: 'blue'},
{name: 'Item 2', color: 'red'}
]
$scope.addItem = function() {
$scope.items.push({name: 'Another item', color: 'black'})
}
}
Thanks for your help!
a) as far as i know this can only be achieved with a negative margin-top transitions to your target value 5px. There is one problem with this: you have to know the exact height of one item.
#-webkit-keyframes slide_down {
0% {
margin-top: -68px;
opacity: 0;
z-index: -100;
}
99% {
z-index: -100;
}
100% {
margin-top:5px;
opacity: 1;
z-index: 0;
}
}
b) you can use the $animate service. The service has an enabled method, so you may disable or enable the animations at any time. here is your controller code that disables the animation on start and after the start enables the animation. the trick is the $timeout service. with that you defer activating the animations until the next digest cycle happens:
function MyCtrl($scope, $animate, $timeout) {
$animate.enabled(false);
$scope.name = 'foo';
$scope.items = [
{name: 'Item 1', color: 'blue'},
{name: 'Item 2', color: 'red'}
]
$scope.addItem = function() {
$scope.items.push({name: 'Another item', color: 'black'})
}
$timeout(function(){
$animate.enabled(true);
});
}
here is the working example:
http://jsfiddle.net/CKF47/