How to prevent a rotated div from moving when changing its width? - javascript

I have a reproduction of the issue I'm facing below:
.rect {
transform: rotateZ(45deg);
background-color: #ddd;
position: absolute;
width: 300px;
height: 200px;
animation: 3s linear 0s infinite normal none running widen;
}
#keyframes widen {
from { width: 300px; }
to { width: 500px; }
}
<div class="rect"></div>
Notice how as the rectangle gets wider, its position also changes. It is true that the element's programmatic position isn't changing, but visually on the screen it appears it is.
I am only looking to widen it without having it visually appear to move. That is, the right side of the rectangle should simply extrude out an additional 200 pixels. Is this possible?
Looking around, it seems like setting the transform-origin from center to top left accomplishes this effect, but unfortunately my rectangle's origin of rotation is around its center. Perhaps the solution is to temporarily set it in some way while the effect is occurring..

An easy way to do this is to wrap the grey rectangle div in another div that does the rotating. That way the width and rotation can be controlled independently.
If other transformations need to be applied as well, this approach gives you control over the order the transformations will be applied.
.rect {
transform: rotateZ(45deg);
}
.widen {
background-color: #ddd;
width: 300px;
height: 200px;
animation: 3s linear 0s infinite normal none running widen;
}
#keyframes widen {
from {
width: 300px;
}
to {
width: 500px;
}
}
<div class="rect">
<div class="widen" />
</div>

That squish effect is expected, but for this scenario I'd suggest not animating the size properties anyway since it's going to push your natural DOM flow around with if there's other elements. Instead try scale transform, see below.
.rect {
transform: rotate(45deg);
background-color: #ddd;
position: absolute;
width: 300px;
height: 200px;
transform-origin: center left;
animation: 3s linear widen infinite;
}
/*
#keyframes widen {
from { width: 300px; }
to { width: 500px; }
}
*/
#keyframes widen {
to { transform: rotate(45deg) scaleX(2);
}
<div class="rect"></div>

It looks like it is moving because of the angle. If you want it to grow proportionally, add height to the animation.
Example 1:
.rect {
transform: rotateZ(45deg);
background-color: #ddd;
position: relative;
width: 300px;
height: 200px;
animation: 3s linear 0s infinite normal none running widen;
}
#keyframes widen {
from { width: 300px; height: 200px; }
to { width: 500px; height: 400px; }
}
<div class="rect"></div>
If we remove the rotation, it won't look like it is moving.
Example 2:
.rect {
background-color: #ddd;
position: relative;
width: 300px;
height: 200px;
animation: 3s linear 0s infinite normal none running widen;
}
#keyframes widen {
from { width: 300px; }
to { width: 500px; }
}
<div class="rect"></div>

Related

Is there any way to animate transform-origin in combination with transform: scale?

So I have an image viewer which has a zoom functionality, which works via the transform: scale() property.
Animating the zoom is no problem with transition: transform .3s.
To make the zoom on the mousewheel go to where the mousewheel is pointed, I calculated the correct position to set the new origin, but when I set it, it just jumps there with no animation.
What I have tried:
Setting transition for the transform-origin property → Doesn't work
Doing it manually in JS with setTimeout and slowly setting the transform-origin at the right position → plays the zoom animation and then jumps
Is there any way to animate both transform: scale() and transform-origin in one go?
Dupe
As the last question has been closed as a duplicate of How to have multiple CSS transitions on an element?, here is a snippet, to show you that this is not my question.
const img = document.querySelector('#container img');
let on = true;
const toggleEffect = () => {
if(on) {
img.style.transform = 'scale(2.5)';
img.style.transformOrigin = '80% 80%';
} else {
img.style.transform = 'scale(1.4)';
img.style.transformOrigin = '20% 20%';
}
on = !on;
};
#container {
width: 500px;
height: 300px;
overflow: hidden;
background-color: red;
}
#container img {
height: 100%;
width: 100%;
object-fit: contain;
transform: scale(1.4);
transform-origin: 20% 20%;
transition: transform .3s, transform-origin .3s;
}
<div id="container">
<img src="https://images.unsplash.com/photo-1482066490729-6f26115b60dc?ixlib=rb-1.2.1&auto=format&fit=crop&w=2004&q=80"/>
</div>
<button onclick="toggleEffect()">Toggle</button>
EDIT: Technically this is a duplicated. (Chrome Bug in some versions)
- Using both transition:
body, div {
width: 100%;
height: 100vh;
overflow: hidden;
}
div {
background-color: gray;
width: auto;
display: flex;
justify-content: center;
align-items: center;
font-size: 30px;
transform: scale(1.1);
transform-origin: 50% -30px -100px;
transition: transform-origin .2s ease-in-out, transform 4s ease-in-out;
}
div:hover {
transform: scale(1.7);
transform-origin: 100px 100px;
}
<div>Test</div>
- Using animation with#keyframes:
body,div {
width: 100%;
height: 100vh;
}
div {
width: auto;
display: flex;
justify-content: center;
align-items: center;
transform-origin: 0 0 0;
animation: scale-origin 3s infinite;
font-size: 30px;
}
#keyframes scale-origin {
0% {
transform: scale(.5);
transform-origin: 100px 100px 1000px;
}
100% {
transform: scale(1.1);
transform-origin: left 500px -30px
}
}
<div>Test</div>
For me the only way to get around this bug was to ensure a redraw of the element on each "animation" (in this case transition) frame as you can clearly see via getComputedStyle that the transform-origin is correctly transitioned!
Basically I added eventlisteners for the transitionstart and transitionend and on each animationframe toggle some style attribute that enforces a redraw (f.e. in my case margin-left from 0 to 1 to 0px until the animation is finished)
function forceRedraw(ts) {
this.style.marginLeft = this.style.marginLeft == '1px' ? '0px':'1px';
if (this.classList.contains('transitioning'))
requestAnimationFrame(forceRedraw.bind(this));
}
In my example I transition rotation and the transform-origin (from top left to bottom left) at the same time.
https://codepen.io/ftav/pen/QWvYEPj
Depending on which element you modify this might have more or less of a performance impact. It works fine for me. I just wish they would fix the bug and this workaround could go away.

Unable to center a loader on a video

I have a video and i have a loader for the buffer. i cannot seem to center it. Iv tried countless methods iv found online and nothing seems to center it.
I have tried every single method from here: http://vanseodesign.com/css/vertical-centering/
I have tried every method from here: https://www.w3.org/Style/Examples/007/center.en.html
I have tried every method on here: https://www.w3schools.com/css/css_align.asp
No joke im not kidding. i have tried everything.
Nothing is working....
Also on mobile that blue loader ends up at the bottom of the video and not centered meaning i cannot use margins because it needs to be centered for every single screen size. Though the spinner class requires this margin margin: 100px auto; or the loader wont show up.....
It needs to have Position: absolute; on the parent div.
It also needs to have display: none; on the parent div so it is hidden until the javascript calls it. Now No matter what i do i keep getting this:
Desktop Problem image <--
Mobile problem image <--
The "Hello how are you" is centered. The blue loader is not. The blue loader needs to be centered.
It must work in a grid similar to what i made here: https://jsfiddle.net/9faxe587/2/
var video = document.getElementById("video_1");
var placeholder = document.getElementById("placeholder_1");
placeholder_1.style.top = video_1.offsetTop + "px";
placeholder_1.style.left = video_1.offsetLeft + "px";
video_1.onwaiting = function() {
showPlaceholder(placeholder_1, this);
};
video_1.onplaying = function() {
hidePlaceholder(placeholder_1, this);
};
function showPlaceholder(img, vid) {
img.style.height = vid.scrollHeight + "px";
img.style.width = vid.scrollWidth + "px";
img.style.display = "block";
}
function hidePlaceholder(img, vid) {
img.style.display = "none";
}
.spinner {
margin: 100px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px;
}
.spinner>div {
background-color: #0080ff;
height: 100%;
width: 5.5px;
display: inline-block;
-webkit-animation: sk-stretchdelay 1.2s infinite ease-in-out;
animation: sk-stretchdelay 1.2s infinite ease-in-out;
}
.spinner .rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.spinner .rect3 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.spinner .rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.spinner .rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
#-webkit-keyframes sk-stretchdelay {
0%,
40%,
100% {
-webkit-transform: scaleY(0.4)
}
20% {
-webkit-transform: scaleY(1.0)
}
}
#keyframes sk-stretchdelay {
0%,
40%,
100% {
transform: scaleY(0.4);
-webkit-transform: scaleY(0.4);
}
20% {
transform: scaleY(1.0);
-webkit-transform: scaleY(1.0);
}
}
.THG-placeholder {
display: none;
position: absolute;
z-index: 1;
}
.THG-video {
width: 100% !important;
height: auto !important;
max-height: 380px;
max-width: 512px;
z-index: 1;
}
<video class="THG-video" id="video_1" controls preload="none">
<source src="http://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4" />
</video>
<div id="placeholder_1" class="THG-placeholder">
<div class="spinner">
<div class="rect1"></div>
<div class="rect2"></div>
<div class="rect3"></div>
<div class="rect4"></div>
<div class="rect5"></div>
</div>
</div>
I have updated #DanteTheSmith's fiddle with my solution using Flexbox.
Fiddle: https://jsfiddle.net/Ldhoo0f6/5/
.THG-placeholder {
top: 0;
left: 0;
width: 100%;
display: none; // show with display: flex;
align-items: center;
justify-content: center;
height: Calc(100% - 36px);
position: absolute;
z-index: 1;
}
video {
width: 100%;
}
.wrapper {
position: relative;
max-width: 460px; // or a percentage or whatever you like
}
Explanation
There is a wrapper div with position: relative around the whole thing, so the absolutely positioned element will be within that div's constraints.
The video is 100% width so it is responsive.
The wrapper div determines the size of the video with max-width
The placeholder element has display: flex added by js.
It is centred with Flexbox align-items and justify-content.
The height uses Calc to adjust for the height of the video controls - though this might differ depending on the browser (I used Chrome). You could just use 100% if you're not too pedantic.
Flexbox is pretty well supported these days: https://caniuse.com/#feat=flexbox
.spinner {
margin: 55px auto; // (video height / 2) - (spinner height / 2)
height: 40px;
text-align: center;
font-size: 10px;
}
If you wanna center the element in entire video box (including the controls)
.spinner {
margin: 32px auto; // (video height - controls height - 45px / 2) - (spinner height / 2)
height: 40px;
text-align: center;
font-size: 10px;
}
Working with second CSS:
fidd

When background image scaled, it starts to flicker in Chrome

I have a div with a background image on it. When it has simple transform scale animation, it starts to flicker in Google Chrome and Opera.
Here is a simple exmple:
http://codepen.io/anon/pen/bWpNYq
CSS:
body {
height: 100vh;
overflow: hidden
}
div {
width: 100%;
height: 100%;
background-color: #f00;
background-position: 50% 50%;
background-image: url(".....jpg");
background-size: cover;
}
Script:
TweenLite.set('div', {
backfaceVisibility: 'hidden',
perspective: 1000
});
TweenLite.fromTo('div', 10, {
scale: 1.1
}, {
scale: 1
});
When the image is a simple img element, the same scale animation works fine. The transition is smooth:
http://codepen.io/anon/pen/pPyvdp
The examples use GASP for animations. I need a solution which use GSAP to scale the div with better result.
Do you any idea how to make it smooth with background image?
Try this:
Add transition: all 1s linear; so it scale smoothly.
body {
height: 100vh;
overflow: hidden
}
div {
width: 100%;
height: 100%;
background-position: 50% 50%;
background-image: url("https://smartslider3.com/wp-content/uploads/2015/10/slide52.jpg");
background-size: cover;
transition: all 1s linear;
}
Hey maybe you can try out this css animation. For better browser support add
-webkit-animation
-moz-animation
-o-animation
body {
height: 100vh;
overflow: hidden
}
div {
width: 100%;
height: 100%;
background-position: 50% 50%;
background-image: url("https://smartslider3.com/wp-content/uploads/2015/10/slide52.jpg");
background-size: cover;
-webkit-animation: animate 5s forwards;
animation: animate 5s forwards;
}
#-webkit-keyframes animate {
0% { transform: scale(1); }
100% { transform: scale(1.1); }
}
#keyframes animate {
0% { transform: scale(1); }
100% { transform: scale(1.1); }
}
<div>
</div>
CSS3 allows you to add native transition to your transformations. Try to use code below:
document.body.addEventListener('click', function(){
var div = document.getElementById('img');
div.style.transform = 'scale(.5)';
})
body {
height: 100vh;
overflow: hidden
}
div {
width: 100%;
height: 100%;
background-position: 50% 50%;
background-image: url("https://smartslider3.com/wp-content/uploads/2015/10/slide52.jpg");
background-size: cover;
transition: transform 30s;
}
<div id="img"></div>
It uses css property "transition" and starts transition on body click.
Just use css, way better. If you open up your inspector you'll see that your tweenlite code is setting/ updating the style attribute of your div very fast with this piece of code: transform: translate3d(0px, 0px, 0px) scale(1.00212, 1.00212);.
This is JS calculating something and then telling CSS what to do (very basic explanation). CSS can do this on it's own. Why do you want to stick with your GSAP engine so badly?

CSS3 Animation: Forwards fill not working with position and display on Safari

So, I have created a CSS3 animation that is supposed to fade out an element by setting its opacity from 1 to 0 and at the last frames change the position to absolute and display to none. But on Safari it will only maintain the opacity, position and display are not set to the final values.
#-webkit-keyframes impressum-fade-out {
0% {
opacity: 1;
display: block;
position: relative;
}
99% {
opacity: 0;
position: relative;
}
100% {
opacity: 0;
display: none;
position: absolute;
}
}
It seems to work on Chrome but not on Safari (I tried version 8). Apparently, position and display do not work properly with animation-fill-mode: forwards...
JSFiddle: http://jsfiddle.net/uhtL12gv/
EDIT For Bounty: I am aware of workarounds with Javascript and transitionend events. But I am wondering why Browsers lack support for this? Does the specification state that fillmode forwards doesnt apply to some attributes like position or is this a bug in the browsers? Because I couldnt find anything in the bug trackers.. If anybody has some insight, I would really appreciate it
As Suggested in the comments, you can adjust the height.
EDIT: Animation Reference Links Added.
Display property is not animatable.
Position property is not
animatable.
List of all CSS properties and if and how they are
animatable.
$('.block').click(function() { $(this).toggleClass('active') });
#-webkit-keyframes impressum-fade-out {
0% {
opacity: 1;
}
99% {
opacity: 0;
}
100% {
opacity: 0;
height:0;
}
}
.block {
width: 100px;
height: 100px;
background: blue;
}
.block2 {
width: 100px;
height: 100px;
background: red;
}
.block.active {
-webkit-animation-name: impressum-fade-out;
animation-name: impressum-fade-out;
-webkit-animation-duration: 500ms;
animation-duration: 500ms;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="block"></div>
<div class="block2"></div>
I would suggest you the cross-browser solution based on CSS3 Transitions and transitionend event:
JSFiddle
$('.block').one('click', function() {
var $this = $(this);
$this.one('webkitTransitionEnd transitionend', function() {
$this.addClass('block_hidden');
$this.removeClass('block_transition');
});
$this.addClass('block_transition');
});
.block {
width: 100px;
height: 100px;
background: blue;
-webkit-transition: opacity 0.5s;
transition: opacity 0.5s;
}
.block_2 {
background: red;
}
.block_transition {
opacity: 0;
}
.block_hidden {
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="block"></div>
<div class="block block_2"></div>

Zoom only a specific portion of an image in pure CSS

I have a div with a width of 800 and a height of 300 pixels.
I also have an .svg image that's set as the background-image of this div, and using css3 animations I make this image scroll left to right, indefinitely (it's a landscape) and wrapping.
I would like to put a circle in the middle of this div, and make the inside of this circle "zoom" the background. I'd love to have this pure CSS.
I've tried some masking and clipping, but nothing seemed to do the trick.
Is this possible with the current CSS specifications? A JavaScript solution would also be acceptable.
Here's an image showing what I mean:
If you look closely, you can see a circle in the middle, which should zoom the clouds behind it, as if looking through a magnifying glass.
Trying to get it reusing the same animation, without extra elements:
CSS
.test {
position: absolute;
width: 600px;
height: 400px;
left: 0px;
background-image: url(http://placekitten.com/1000/400);
background-size: 1000px;
-webkit-animation: base linear 20s infinite;
background-position-x: 0px;
background-position-y: 50%;
}
.test:after {
content: "";
position: absolute;
left: 200px;
width: 200px;
top: 100px;
height: 200px;
border-radius: 50%;
background: inherit;
-webkit-transform: scale(1.1);
-webkit-animation: inherit;
-webkit-animation-delay: -4s;
}
#-webkit-keyframes base {
0% { background-position-x: 0px; }
100% { background-position-x: -1000px; }
}
The trick is to set the animation in sync delaying it; just calculate the equivalence in time of the x offset.
fiddle
throw your zoom div into the pic div and give it a background image of a larger version of the same image.

Categories

Resources