GSAP only apply scaling while mouse button is held - javascript

I have a horizontal slider with sections that scale when dragged using GSAP. The issue I have is the scaling is applied on click and the value is saved until after dragging. I need the scaling to only be applied while the mouse button is held down.
Here is a codepen.
var drag = Draggable.create(".horizontalContainer ", {
type: "x",
onPress() {
const scale = .6;
gsap.to('.section', {scale: scale, duration: .25});
},
onDragEnd() {
const xpos = this.target.getBoundingClientRect().left;
const winWidth = window.innerWidth;
gsap.to('.section', {scale: 1, duration: .25});
gsap.to(this.target, {x: Math.round(xpos / winWidth) * winWidth, duration: .5});
}
});
body {
margin: 0;
padding: 0;
overflow: hidden;
}
.horizontalContainer {
width: 600%;
display: flex;
flex-wrap: nowrap;
}
.section {
height: 100vh;
width: 100vw;
text-align: center;
font-size: 36px;
line-height: 90vh;
}
.section:nth-child(1) {
background-color: #deb887;
}
.section:nth-child(2) {
background-color: limegreen;
}
.section:nth-child(3) {
background-color: #b8860b;
}
.section:nth-child(4) {
background-color: #2f4f4f;
}
.proxy {
position: absolute;
visibility: hidden;
}
<script src="https://unpkg.com/gsap#3/dist/Draggable.min.js"></script>
<script src="https://unpkg.com/gsap#3/dist/ScrollTrigger.min.js"></script>
<script src="https://unpkg.co/gsap#3/dist/gsap.min.js"></script>
<div class='horizontalContainer'>
<div class='section'>ScrollTrigger with Draggable</div>
<div class='section'></div>
<div class='section'></div>
<div class='section'></div>
</div>

Use onRelease() callback to bring the scale back to 1.
onRelease() {
const scale = 1;
gsap.to('.section', {scale: scale, duration: .25});
}
Code Snippet:
var drag = Draggable.create(".horizontalContainer ", {
type: "x",
onPress() {
const scale = .6;
gsap.to('.section', {
scale: scale,
duration: .25
});
},
onRelease() {
const scale = 1;
gsap.to('.section', {
scale: scale,
duration: .25
});
},
onDragEnd() {
const xpos = this.target.getBoundingClientRect().left;
const winWidth = window.innerWidth;
gsap.to('.section', {
scale: 1,
duration: .25
});
gsap.to(this.target, {
x: Math.round(xpos / winWidth) * winWidth,
duration: .5
});
}
});
body {
margin: 0;
padding: 0;
overflow: hidden;
}
.horizontalContainer {
width: 600%;
display: flex;
flex-wrap: nowrap;
}
.section {
height: 100vh;
width: 100vw;
text-align: center;
font-size: 36px;
line-height: 90vh;
}
.section:nth-child(1) {
background-color: #deb887;
}
.section:nth-child(2) {
background-color: limegreen;
}
.section:nth-child(3) {
background-color: #b8860b;
}
.section:nth-child(4) {
background-color: #2f4f4f;
}
.proxy {
position: absolute;
visibility: hidden;
}
<div class='horizontalContainer'>
<div class='section'>ScrollTrigger with Draggable</div>
<div class='section'></div>
<div class='section'></div>
<div class='section'></div>
</div>
<script src="https://unpkg.com/gsap#3/dist/Draggable.min.js"></script>
<script src="https://unpkg.com/gsap#3/dist/ScrollTrigger.min.js"></script>
<script src="https://unpkg.co/gsap#3/dist/gsap.min.js"></script>

Related

CSS: How to don't reset onhover animation on every hover

I have a simple on-hover CSS animation which makes slide transition between images.
When the user makes the hover on SECTION ONE and before the animation ends make hover on SECTION two, animation restart and make lagging move.
MY CODE:
var $circle = $('#circle');
function moveCircle(e) {
TweenLite.to($circle, 0.8, {
css: {
left: e.pageX,
top: e.pageY
}
});
}
$(window).on('mousemove', moveCircle);
#import "compass/css3";
#keyframes in {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
#keyframes out {
from {
transform: translateY(0);
}
to {
transform: translateY(100%);
}
}
html {
background: #0E3741;
}
#circle {
position: absolute;
pointer-events : none;
width: 400px;
height: 200px;
top: 50%;
left: 50%;
margin: -50px 0 0 -50px;
}
#circle .circle-wrapper {
overflow: hidden;
width: 400px;
height: 200px;
position: relative;
}
#circle img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 400px;
height: 200px;
object-fit: cover;
overflow: hidden;
}
#wrapper {
display: flex;
flex-direction: column;
}
.special-element {
width: 100%;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
#one {
background: blue;
}
#two {
background: red;
}
#one:hover ~ #circle .circle-wrapper #imgOne {
animation: in 1s ease-in-out;
z-index: 2;
}
#one:hover ~ #circle .circle-wrapper #imgTwo {
animation: out 1s ease-in-out;
}
#two:hover ~ #circle .circle-wrapper #imgTwo {
animation: in 1s ease-in-out;
z-index: 2;
}
#two:hover ~ #circle .circle-wrapper #imgOne {
animation: out 1s ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>
<section id="wrapper">
<section class="special-element" id="one">
section one
</section>
<section class="special-element" id="two">
section two
</section>
<div id="circle">
<div class="circle-wrapper">
<img id="imgOne" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
<img id="imgTwo" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
</div>
</div>
</section>
Is there any solution how I can prevent this lagging issue?
Maybe is there any solution to how I can solve it and make this animation smooth?
I'm looking for something like animation on this website.
Updated version
You can do a simplified version of it with gsap. It is probably best not to mix plain css too much with the gsap, unless you use css inside the gsap library. Because gsap will manipulate some of the props. E.g. the transformation. And it is better to use transform than just left/top because it is hardware accelerated.
I've done some improvements to the code I've posted before. It looks smoother now. In addition, I have added a little zoom and horizontal-shift effect - similar to the animation on the referenced website. Also, the animation now starts from the bottom.
The animation is really well done on the referenced page. It is done with WebGL. This is not your every-day animation and requires quite a bit of effort to make it work - at least for someone who is not a designer. It uses a 3d transformation matrix and some other effects together.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset-css#5.0.1/reset.min.css" />
<script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
<style type="text/css">
.section {
display: block;
width: 100%;
height: 300px;
border-bottom: 1px solid red;
}
.overlay {
position: absolute;
top: 0;
left: 0;
display: none;
background: transparent;
z-index: -1;
}
.stack {
position: relative;
min-width: 300px;
min-height: 300px;
width: 480px;
height: 320px;
max-width: 480px;
max-height: 320px;
overflow: hidden;
z-index: 1;
}
.img {
position: absolute;
top: 0;
left: 0;
width: auto;
height: auto;
max-width: 100%;
object-fit: contain;
z-index: -1;
}
</style>
</head>
<body>
<main class="main">
<section class="section section-1" data-img="img-1">section 1</section>
<section class="section section-2" data-img="img-2">section 2</section>
<div class="overlay">
<div class="stack">
<img id="img-1" class="img" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
<img id="img-2" class="img" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
</div>
</div>
</main>
<script type="application/javascript">
window.onload = () => {
const overlay = document.querySelector(".overlay");
const stack = document.querySelector(".stack");
const s1 = document.querySelector(".section-1");
const s2 = document.querySelector(".section-2");
const main = document.querySelector(".main");
const overlaySize = {
width: 480,
height: 320
};
const easeFunc = "sine.inOut";
const easeDuration = 0.5;
let animation;
let activeSection;
let currentTarget;
function createAnimation() {
//console.log('create animation');
t1 = gsap.timeline({
paused: true
});
t1.to(currentTarget, {
zIndex: 2,
display: "block"
}, 0);
t1.fromTo(currentTarget, {
y: "100%"
}, {
y: 0,
duration: easeDuration,
ease: easeFunc
}, 0);
t1.to(currentTarget, {
scale: 1.25,
transformOrigin: "center",
duration: easeDuration,
ease: easeFunc
}, 0);
stack.querySelectorAll(".img").forEach((it) => {
if (it !== currentTarget) {
t1.to(it, {
zIndex: -1
}, 0);
t1.to(it, {
scale: 1,
transformOrigin: "center"
}, 0);
t1.to(it, {
display: "none"
}, easeDuration);
}
});
return t1;
}
function onMouseLeave(e) {
const target = e.target;
//console.log("leave", e.target);
if (target === activeSection) {
gsap.set(overlay, {
display: "none"
});
currentTarget = null;
}
}
function onMouseEnter(e) {
currentTarget = stack.querySelector(`#${e.target.dataset.img}`);
gsap.set(overlay, {
display: "block"
});
if (!animation) {
//console.log("undefined animation")
animation = createAnimation();
animation.play();
} else if (animation.isActive()) {
//console.log("still active");
animation.timeScale(10); // fast forward the rest of the animation
animation = createAnimation();
animation.timeScale(1).play();
} else {
//console.log("no longer active");
animation = createAnimation();
animation.play();
}
}
function onMouseMove(e) {
const hoveredEl = document.elementFromPoint(e.pageX, e.pageY);
if (hoveredEl.classList.contains("section")) {
if (activeSection !== hoveredEl) {
activeSection = hoveredEl;
}
} else if (hoveredEl.classList.contains("overlay") || hoveredEl.classList.contains("stack") || hoveredEl.classList.contains("pointer")) {
// do nothing
} else {
if (activeSection) {
activeSection = null;
}
}
if (currentTarget) {
// update overlay
gsap.set(overlay, {
x: e.pageX - overlaySize.width / 2,
y: e.pageY - overlaySize.height / 2
});
// add a little horizontal-shift effect
const dx = window.innerWidth / 2 - e.pageX;
const offsetX = dx / window.innerWidth / 2 * 100;
gsap.to(currentTarget, {
x: offsetX * 2,
duration: 2
}, 0);
}
}
gsap.set(overlay, {
x: 0,
y: 0
});
stack.querySelectorAll('.img').forEach((it) => gsap.set(it, {
x: 0,
y: "100%"
}));
window.addEventListener("mousemove", onMouseMove);
s1.addEventListener("mouseleave", onMouseLeave);
s2.addEventListener("mouseleave", onMouseLeave);
s1.addEventListener("mouseenter", onMouseEnter);
s2.addEventListener("mouseenter", onMouseEnter);
}
</script>
</body>
</html>
Old answer
I have been playing around a little bit with the gsap library today. I've honestly never done anything with or like it. Tried to do it with the x and y params that you may pass to gsap. It will take care of the transformations - also the TimeLine stuff
is quite handy. The result is not that great, also the animations look like it could be done better, but maybe it might still help you out. You could also improve some of the logic and animation probably. At least it runs quite stable - performance wise.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/reset-css#5.0.1/reset.min.css" />
<script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
<script type="application/javascript" defer src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/plugins/CSSPlugin.min.js"></script>
<style type="text/css">
.section {
display: block;
width: 100%;
height: 200px;
border-bottom: 1px solid red;
}
.overlay {
position: absolute;
top: 0;
left: 0;
display: none;
border: none; // 1px dashed black;
background: transparent; // lavender;
overflow: hidden;
}
.stack {
position: relative;
left: 0;
right: 0;
top: 0;
bottom: 0;
min-width: 300px;
min-height: 300px;
width: 480px;
height: 320px;
z-index: 0;
}
.anim-img {
position: absolute;
top: 0;
left: 0;
width: auto;
height: auto;
max-width: 100%;
object-fit: contain;
z-index: 1;
}
</style>
</head>
<body>
<main class="main">
<section class="section section-1">section 1</section>
<section class="section section-2">section 2</section>
<div class="overlay">
<div class="stack">
<img id="img-1" class="anim-img" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
<img id="img-2" class="anim-img" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
</div>
</div>
</main>
<script type="application/javascript">
window.onload = () => {
const overlay = document.querySelector(".overlay");
const img1 = document.getElementById("img-1");
const img2 = document.getElementById("img-2");
const s1 = document.querySelector(".section-1");
const s2 = document.querySelector(".section-2");
const main = document.querySelector(".main");
let anim;
let isS1active = false;
let isS2active = false;
let showEl;
let hideEl;
let leaveTimeout;
function reverseFadeInOut(showEl, hideEl) {
console.log("create reverse timeline anim -> ", {
showEl,
hideEl
});
const tl = gsap.timeline({
paused: true
});
tl
.to(showEl, {
zIndex: 1
}, 0)
.to(hideEl, {
zIndex: 10
}, 0)
.to(hideEl, {
y: "-100%",
duration: 0.375
}, 0)
.to(hideEl, {
display: "none"
}, 0.375)
.to(hideEl, {
zIndex: 1
}, 0.375)
.to(showEl, {
display: "block",
zIndex: 10
}, 0.375)
.fromTo(showEl, {
y: "-100%"
}, {
y: 0,
duration: .375
}, 0.375)
.to(hideEl, {
display: "none"
});
return tl;
}
function fadeInOut(showEl, hideEl) {
console.log("create timeline anim -> ", {
showEl,
hideEl
});
const tl = gsap.timeline({
paused: true
});
tl
.to(hideEl, {
zIndex: 1
}, 0)
.to(showEl, {
display: "block",
zIndex: 10
}, 0)
.fromTo(showEl, {
y: "-100%"
}, {
y: 0,
duration: .75
}, 0)
.fromTo(hideEl, {
y: 0
}, {
y: "-100%",
duration: .75
}, 0)
.to(hideEl, {
display: "none"
}, 0.75);
return tl;
}
function animateImage() {
if (isS1active || isS2active) {
if (isS1active) {
showEl = img1;
hideEl = img2;
} else if (isS2active) {
showEl = img2;
hideEl = img1;
}
if (!anim) {
console.log("create new animation");
anim = fadeInOut(showEl, hideEl);
anim.play();
} else {
console.log("anim active:", anim.isActive());
if (anim.isActive()) {
console.log("reverse");
anim.kill();
anim = reverseFadeInOut(showEl, hideEl);
anim.play();
} else {
anim = fadeInOut(showEl, hideEl);
anim.play();
}
}
}
}
function moveOverlay(e) {
e.preventDefault();
e.stopPropagation();
gsap.set(overlay, {
x: e.pageX + 15,
y: e.pageY + 15,
display: isS1active || isS2active ? "block" : "none"
});
}
function mouseOver(e, el, isEntering) {
e.preventDefault();
e.stopPropagation();
el.classList.toggle("active");
isS1active = s1.classList.contains("active");
isS2active = s2.classList.contains("active");
if (isEntering) {
clearTimeout(leaveTimeout);
animateImage();
} else {
leaveTimeout = setTimeout(() => {
if (anim) {
console.log("kill anim");
anim.kill();
anim = null;
}
gsap.timeline({
onComplete: () => {
console.log("clear props");
gsap.set(".anim-img", {
clearProps: true
});
}
});
}, 500);
}
}
gsap.set(overlay, {
x: "0",
y: "0"
});
gsap.set(img1, {
x: "0",
y: "-100%"
});
gsap.set(img2, {
x: "0",
y: "-100%"
});
window.addEventListener("mousemove", moveOverlay);
s1.addEventListener("mouseenter", (e) => {
mouseOver(e, s1, true);
});
s1.addEventListener("mouseleave", (e) => {
mouseOver(e, s1, false);
});
s2.addEventListener("mouseenter", (e) => {
mouseOver(e, s2, true);
});
s2.addEventListener("mouseleave", (e) => {
mouseOver(e, s2, false);
});
}
</script>
</body>
</html>
I think that problem is because "moving circle function". Moving dom element with Left and right is not good for performance. You should move the circle with "transform". transform runs with GPU acceleration and it performs better and make move smooth.
Try this code.
function moveCircle(e) {
TweenLite.to($circle, 0.8, {
css: {
transform: `translate(${e.pageX}px, ${e.pageY}px)`
}
});
}
Yes, there is, by modifying the value of the second parameter of TweenLite.to, because that's the duration, see more here: http://www.tud.ttu.ee/im/Jaak.Henno/FlashDevelop/greensock-as3/greensock-as3/docs/com/greensock/TweenLite.html#to()
var $circle = $('#circle');
function moveCircle(e) {
TweenLite.to($circle, 0.1, {
css: {
left: e.pageX,
top: e.pageY
}
});
}
$(window).on('mousemove', moveCircle);
#import "compass/css3";
#keyframes in {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
#keyframes out {
from {
transform: translateY(0);
}
to {
transform: translateY(100%);
}
}
html {
background: #0E3741;
}
#circle {
position: absolute;
pointer-events : none;
width: 400px;
height: 200px;
top: 50%;
left: 50%;
margin: -50px 0 0 -50px;
}
#circle .circle-wrapper {
overflow: hidden;
width: 400px;
height: 200px;
position: relative;
}
#circle img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 400px;
height: 200px;
object-fit: cover;
overflow: hidden;
}
#wrapper {
display: flex;
flex-direction: column;
}
.special-element {
width: 100%;
height: 100px;
display: flex;
justify-content: center;
align-items: center;
}
#one {
background: blue;
}
#two {
background: red;
}
#one:hover ~ #circle .circle-wrapper #imgOne {
animation: in 1s ease-in-out;
z-index: 2;
}
#one:hover ~ #circle .circle-wrapper #imgTwo {
animation: out 1s ease-in-out;
}
#two:hover ~ #circle .circle-wrapper #imgTwo {
animation: in 1s ease-in-out;
z-index: 2;
}
#two:hover ~ #circle .circle-wrapper #imgOne {
animation: out 1s ease-in-out;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>
<section id="wrapper">
<section class="special-element" id="one">
section one
</section>
<section class="special-element" id="two">
section two
</section>
<div id="circle">
<div class="circle-wrapper">
<img id="imgOne" src="https://upload.wikimedia.org/wikipedia/commons/3/3b/Coca-cat.jpg">
<img id="imgTwo" src="https://staticcdn.sk/images/photoarchive/sized/700/2020/07/29/ohrozeny-vtak-krakla-belasa.jpg">
</div>
</div>
</section>
The website is using canvas for the moving slideshow and they've used webgl for the smooth kind of reveal effects.
<div class="site-footer">
<div class="js-scroll-height"></div>
<canvas width="780" height="624" class="js-webgl" style="width: 1041px;height: 833px;opacity: 1;border: 1px solid brown;"></canvas>
</div>
We can create similar reveal effect using simple animation and z-index manipulation:
var $cursor = $('#cursor');
function movecursor(e) {
TweenLite.to($cursor, 0.8, {
css: {
left: e.pageX,
top: e.pageY
}
});
}
$(window).on('mousemove', movecursor);
let topImg = null;
$('.special-element').mouseenter((e) => {
//make all images at same level
$('.img-wrapper').css({ zIndex: '1' });
//make last focused image at second highest level
if (topImg) topImg.css({ zIndex: '2' });
//make current image as topImage
let atr = $(e.target).attr('data-img');
topImg = $('.img-wrapper[data-img="' + atr + '"]');
//make it topmost
topImg.css({ zIndex: '3' });
});
:root {
--cursor-img-height: 30vh;
--cursor-img-width: 30vw;
}
html,
body {
background: #0E3741;
}
#wrapper {
display: flex;
flex-direction: column;
}
.special-element {
width: 100%;
height: 33vh;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
}
.special-element:nth-child(1) {
background: lightblue;
}
.special-element:nth-child(2) {
background: lightcoral;
}
.special-element:nth-child(3) {
background: lightgreen;
}
#cursor {
position: absolute;
pointer-events: none;
margin: 0;
margin-top: calc(var(--cursor-img-height) * -0.5);
margin-left: calc(var(--cursor-img-width) * -0.5);
overflow: hidden;
width: var(--cursor-img-width);
height: var(--cursor-img-height);
}
.img-wrapper {
position: absolute;
bottom: 0;
left: 0;
width: var(--cursor-img-width);
height: var(--cursor-img-height);
overflow: hidden;
}
.img-wrapper>img {
position: absolute;
bottom: 0;
left: 0;
width: var(--cursor-img-width);
height: var(--cursor-img-height);
object-fit: fill;
z-index: 1;
}
.special-element[data-img="one"]:hover~#cursor [data-img="one"],
.special-element[data-img="two"]:hover~#cursor [data-img="two"],
.special-element[data-img="three"]:hover~#cursor [data-img="three"] {
animation: slide .8s ease-in-out;
}
#keyframes slide {
from {
height: 0px;
transform: scale(1.2);
}
to {
height: (--cursor-img-height);
transform: scale(1);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.11.4/TweenMax.min.js"></script>
<section id="wrapper">
<section class="special-element" data-img="one">food</section>
<section class="special-element" data-img="two">animal</section>
<section class="special-element" data-img="three">night</section>
<div id="cursor">
<div class="img-wrapper" data-img='one'>
<img src="https://picsum.photos/id/674/400/200">
</div>
<div class="img-wrapper" data-img='two'>
<img src="https://picsum.photos/id/433/400/200">
</div>
<div class="img-wrapper" data-img='three'>
<img src="https://picsum.photos/id/901/400/200">
</div>
</div>
</section>
View in full page mode.

how to make a carousel(sliding) stop when the mouse pointer hovers over on the images

How to make carousel (slider) stop when the mouse pointer has hovered over on the image in GSAP?
I made a huge try in many ways on the internet but nothing suit for my code.
// wrapping the code with onload to execute JS immediately
window.onload = function() {
//variables for slide animation time
var slideDelay = 1.2; //the slides flow in every 1.5 seconds
var slideDuration = 0.2;
var slides = document.querySelectorAll(".slide");
var prevButton = document.querySelector("#prevButton");
var nextButton = document.querySelector("#nextButton");
var numSlides = slides.length;
//infinite slide rotation
for (var i = 0; i < numSlides; i++) {
TweenLite.set(slides[i], {
xPercent: i * 100
});
}
// auto animation (the timer can be added to auto animate after a certain idle-time)
var wrap = wrapPartial(-100, (numSlides - 1) * 100);
var timer = TweenLite.delayedCall(3, autoPlay);
var animation = TweenMax.to(slides, 1, {
xPercent: "+=" + (numSlides * 100),
ease: Linear.easeNone,
paused: true,
repeat: -1,
modifiers: {
xPercent: wrap
}
});
var proxy = document.createElement("div");
TweenLite.set(proxy, { x: "+=0" });
var transform = proxy._gsTransform;
var slideAnimation = TweenLite.to({}, 0.1, {});
var slideWidth = 0;
var wrapWidth = 0;
resize();
window.addEventListener("resize", resize);
// navigation with buttons
prevButton.addEventListener("click", function() {
animateSlides(1);
});
nextButton.addEventListener("click", function() {
animateSlides(-1);
});
function animateSlides(direction) {
timer.restart(true);
slideAnimation.kill();
var x = snapX(transform.x + direction * slideWidth);
slideAnimation = TweenLite.to(proxy, slideDuration, {
x: x,
onUpdate: updateProgress
});
}
// auto-play function for auto animation
function autoPlay() {
animateSlides(-1);
}
function updateProgress() {
animation.progress(transform.x / wrapWidth);
}
function snapX(x) {
return Math.round(x / slideWidth) * slideWidth;
}
//calculating the necessary width for slide animation
function resize() {
var norm = (transform.x / wrapWidth) || 0;
slideWidth = slides[0].offsetWidth;
wrapWidth = slideWidth * numSlides;
TweenLite.set(proxy, {
x: norm * wrapWidth
});
animateSlides(0);
slideAnimation.progress(1);
}
//returns the difference between the passed function's max and min value
function wrapPartial(min, max) {
var diff = max - min;
return function(value) {
var v = value - min;
return ((diff + v % diff) % diff) + min;
}
}
}
* {
box-sizing: border-box;
}
/* the main wrapper box */
main {
display: flex;
position: relative;
flex-direction: column;
width: 300px;
height: 250px;
overflow: hidden;
border: 3px solid #000000;
}
/* the header and navigations */
.controls {
padding: 10px;
display: flex;
align-items: center;
justify-content: space-between;
height: 60px;
min-height: 60px;
border-bottom: 2px solid #000000;
}
.controls button {
width: 30px;
height: 30px;
border-radius: 50%;
border: none;
outline: none;
background-repeat: no-repeat;
background-size: 70%;
cursor: pointer;
background-color: #000;
background-position: center center;
}
.slides-container {
border-top: 2px solid ;
position: relative;
overflow: hidden;
display: flex;
flex: 1;
}
.slide {
position: absolute;
font-size: 90px;
font-weight: 700;
color: rgba(255,255,255,0.9);
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
background-size: cover;
background-repeat: no-repeat;
}
.slides-inner {
position: relative;
height: 100%;
width: 100%;
overflow: hidden;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="style.css">
<title>Varshath Gupta Solution</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/TweenMax.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/plugins/ModifiersPlugin.min.js"></script>
<script type="text/javascript" src="./script.js">
</script>
</head>
<body>
<!-- Main wrapper div for the header and slider components. -->
<div class="wrapper">
<main>
<!-- the header that includes the navigation buttons and the logo -->
<div class="controls">
<button id="prevButton" style="background-image: url(https://drive.google.com/uc?export=view&id=1o6S3UjO-DQFLXAGShYeN4bQXoI0yuxP0);"></button>
<div class="logo"><img width="80" src="https://logos.textgiraffe.com/logos/logo-name/Cartoon-designstyle-cartoon-m.png"/></div>
<button id="nextButton" style="background-image: url( https://drive.google.com/uc?export=view&id=1aptD-5krbPTtFRjZk_b2O8Ikx2x0F2TC);"></button>
</div>
<!-- the slider -->
<div class="slides-container">
<div class="slides-inner">
</div>
</div>
</div>
<div class="slide" style="background-image: url( https://drive.google.com/uc?export=view&id=1ngTIZMgHFHTSQO9vinuB666ruA3l-Vgq)"></div>
</div>
</div>
</div>
</main>
</div>
</body>
</html>

Is there any way to animate a grid of changing colors onto text?

Here's what I have so far:
HTML:
<div id="title-pg">
<h1 id="title">etch-a-sketch</h1>
<div id="title-grid-ctn"></div>
</div>
Javascript:
let titlePage = document.querySelector("#title-grid-ctn");
createTitleGrid();
function createTitleGrid() {
for (i = 0; i < Math.pow(10, 2); i++) {
const titleGrid = document.createElement("div");
titleGrid.classList.add("title-grid");
titlePage.appendChild(titleGrid);
}
}
const titleGrid = titlePage.querySelectorAll(".title-grid");
titleGrid.forEach((titleGrid) => {
titleGrid.animate(
[
{
backgroundColor: `rgb(${rdmNumber()}, ${rdmNumber()}, ${rdmNumber()})`,
},
{
backgroundColor: `rgb(${rdmNumber()}, ${rdmNumber()}, ${rdmNumber()})`,
},
],
{
duration: 2000,
iterations: Infinity,
delay: 0,
easing: "linear",
direction: "alternate",
}
);
});
I'd like to clip the text over the grid of changing colors, but I can't figure out a way to cut the rest of the grid out.
Is this what I'm trying to do possible?
Move your text on top of the grid and use mix-blend-mode: lighten; in your CSS. Take a look at this live snippet:
let titlePage = document.querySelector("#title-grid-ctn");
createTitleGrid();
function createTitleGrid() {
for (i = 0; i < Math.pow(10, 2); i++) {
const titleGrid = document.createElement("div");
titleGrid.classList.add("title-grid");
titlePage.appendChild(titleGrid);
}
}
function rdmNumber() {
return Math.floor(Math.random() * 255);
}
const titleGrid = titlePage.querySelectorAll(".title-grid");
titleGrid.forEach((titleGrid, i) => {
titleGrid.animate(
[
{
backgroundColor: 'red',
},
{
backgroundColor: 'blue',
},
],
{
duration: 2000,
iterations: Infinity,
delay: -i * 1000,
easing: "linear",
direction: "alternate",
}
);
});
body {
margin: 0;
}
#title-pg {
position: relative;
}
#title-grid-ctn {
display: flex;
flex-wrap: wrap;
}
.title-grid {
width: 1.5em;
height: 1.5em;
display: inline-block;
}
#title {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: #fff;
font: 900 4em sans-serif;
margin: 0;
mix-blend-mode: lighten;
display: flex;
align-items: center;
justify-content: center;
}
<div id="title-pg">
<div id="title-grid-ctn"></div>
<h1 id="title">etch-a-sketch</h1>
</div>

CSS JS Fixed position sidebar

I have 2 blocks - one with fixed position and the other next to it. I would like the fixed block to increase in length after clicking the button, and the second block to decrease in size and to click everything again to return it to its original state. Fixed block will be sidebar menu, permanently glued.
Everything should happen at 100% width.
How to do it, but the important thing is that the width of the fixed block and the latter should be specified in pixels, not in percent.
Is this to be done?
jQuery(document).ready(function($) {
jQuery(".btn").click(function() {
var div1 = jQuery(".left");
var div2 = jQuery(".right");
if (div1.width() < 200) {
div1.animate({
width: "25%"
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
div2.animate({
width: "75%"
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
} else {
div1.animate({
width: "5%"
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
div2.animate({
width: "95%"
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
}
});
});
.container {
width: 100%;
margin: 0 auto;
}
.left {
float: left;
position: fixed;
width: 5%;
height: 100%;
background: pink;
z-index: 5;
}
.right {
float: right;
position: relative;
width: 95%;
height: 300px;
background: green;
}
.content {
float: left;
position: relative;
width: 100%;
height: 100px;
background: silver;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="left">
<input type="button" value="click" class="btn">
</div>
<div class="right">
<div class="content"> abc </div>
</div>
</div>
Everything should happen at 100% width.
... in pixels, not in percent.
Are you sure that it's not the opposite? It's way more better to try to make yor css based on % rather that px or rem (unless it's required to do it in px)
In any case, I fixed your code, but in %, from now on you can play with px if that's the requirement:
jQuery(document).ready(function($) {
jQuery(".btn").click(function() {
var leftDiv = jQuery(".left");
var rightDiv = jQuery(".right");
console.log("leftDiv width: " + leftDiv.width() +
" - " + "rightDiv width: " + rightDiv.width())
var minWidthForRight = $('.container').width() - 300;
var maxWidthForRight = $('.container').width() - 100;
if (leftDiv.width() < 200) {
leftDiv.animate({
width: "300px",
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
rightDiv.animate({
width: minWidthForRight
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
} else {
leftDiv.animate({
width: "100px"
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
rightDiv.animate({
width: maxWidthForRight
}, {
duration: 200,
specialEasing: {
width: "linear"
}
});
}
});
});
.container {
width: 100%;
margin: 0 auto;
}
.left {
float: left;
position: fixed;
width: 150px;
height: 300px;
background: pink;
z-index: 5;
}
.right {
float: right;
position: relative;
width: 95%;
height: 300px;
background: green;
resize: horizontal;
}
.content {
float: left;
position: relative;
width: 100%;
height: 100px;
background: silver;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="left">
<input type="button" value="click" class="btn">
</div>
<div class="right">
<div class="content"> abc </div>
</div>
</div>
EDIT
Yes, it is possible to keep the left side in px and the left side in %, not % itself but calculated in px based on screen resolution.
Basically you take the container width and "eliminate" the width that you specified in animation:
var minWidthForRight = $('.container').width() - 300;
The right side will be: 300 + (100% - 300 ) and same for left: 100 + (100% - 100 )

Change transition duration during loop with requestAnimationFrame

I'm trying to change the transition duration in-between the steps of a setTimeout loop.
When the button centre is clicked, setView changes the position of content so that the inner div remains in the centre of the screen. It uses a forEach loop to change the centring using an array of inner divs.
I'd like the transition between the change from alpha inner to bravo inner to take 1s, but once it gets there the transition should be 0s so that is no lag with the centring.
Here's a codepen, then built in is getting an error.
I'm using vanilla ES6.
const content = document.querySelector('.content');
let frame;
function getBounds(div) {
const element = document.getElementById(div);
const body = element.children[0];
const {
x, y, width, height,
} = body.getBoundingClientRect();
return box = {
element, body, x: Math.round(x), y: Math.round(y), width, height,
};
}
function setView(div) {
const centerW = window.innerWidth / 2;
const centerH = window.innerHeight / 2;
const { left, top } = content.getBoundingClientRect();
content.style.left = `${(centerW + left) - (div.x + div.width / 2)}px`;
content.style.top = `${(centerH + top) - (div.y + div.height / 2)}px`;
}
function centreDiv(string) {
const outers = string.split(' ');
outers.forEach(function(outer, index) {
setTimeout(function() {
content.style.transition = 'all 0s linear'; // this works
if (frame) {
cancelAnimationFrame(frame);
}
function tick(now) {
setView(getBounds(outer));
frame = requestAnimationFrame((timestamp) => tick(timestamp, outer));
}
frame = requestAnimationFrame((timestamp) => tick(timestamp, outer));
},
5000 * index);
content.style.transition = 'all 1s linear'; // this isn't working
});
}
document.getElementById('centre').onclick = () => centreDiv(centreText.value.toLowerCase());
* {box-sizing: border-box;}
html,body,div,span {padding: 0; margin: 0; border: 0;}
.controls {
position: absolute;
z-index: 1000;
padding-top: 10px;
padding-left: 20px;
font-family: sans-serif;
color: white;
}
.content {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100vw;
height:100vh;
background-color: rgb(138,141,143);
transition: all 0s linear;
}
.centre {
position: absolute;
display: block;
width: 5px;
height: 5px;
background-color: white;
}
.outer {
position: absolute;
display: flex;
align-items: center;
justify-content: flex-end;
}
.inner {
display: block;
width: 30px;
height: 30px;
background-color: rgb(37,40,42);
}
.animate {
animation: spin infinite linear;
}
#alpha {
width: 80%;
height: 80%;
background-color: rgb(255,205,0);
animation-duration: 10s;
}
#bravo {
width: 30%;
height: 30%;
background-color: rgb(242,169,0);
animation-duration: 5s;
}
#keyframes spin {
100% {
transform: rotateZ(360deg);
}
}
<div class="controls">
centre: <input id="centreText" type="text" value="alpha bravo"/><button type="button" id="centre">centre</button>
</div>
<div class="content">
<div id="alpha" class="animate outer">
<div class="inner"></div>
</div>
<div id="bravo" class="animate outer">
<div class="inner"></div>
</div>
<div class="centre"></div>
</div>

Categories