I want to add classes on HTML elements one by one.
TweenMax.staggerTo(".some-class-name", 0.5, { className: "+=animation-class" }, 0.5);
I have this code, but it doesn't work. More precisely it works, but it doesn't add className one by one.
How can I achieve an effect like this codepen but with className?
You have a valid Query and if you just want to add a new class to the element. It can be done with TweenMax or TweenLite.
TweenMax
TweenLite.to(element, 0.5, {css:{className:'+=newclass'}});
or
TweenMax.to(element, 0.5, {css:{className:'+=newclass'}});
or
TweenMax.staggerTo(element, 0.5, {css:{className:'+=newclass'}});
You could make a forloop and then pass the class or do a foreach and add this class like this.
for more check out this link :
https://greensock.com/forums/topic/6376-using-greensock-to-change-classes-without-tweening-styles/
Seems fine to me.
Take a look at this jsFiddle.
Or look at the Snippet below.
var duration = 0.5;
var stagger = 0.5;
var someClassName = '.some-class-name';
var animationClass = 'animation-class';
TweenMax.staggerTo(someClassName, duration, {className: '+=' + animationClass, transformOrigin: 'bottom right', ease: Power4.easeOut}, stagger);
html, body { margin: 0; padding: 0; }
.some-class-name {
position: relative;
float: left;
width: 50px;
height: 50px;
background: #0cc;
}
.animation-class {
background: #cc0;
top: 50px;
transform: rotate(90deg);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.17.0/TweenMax.min.js"></script>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
<div class="some-class-name"></div>
var duration = 0.5;
var stagger = 0.5;
var someClassName = '.some-class-name';
var animationClass = 'animation-class';
TweenMax.staggerTo(someClassName, duration, {className: '+=' + animationClass, transformOrigin: 'bottom right', ease: Power4.easeOut}, stagger);
Hope this helps.
Related
I'm trying to do a carousal like in this CodePen using React Hooks.
My present result is in this Sandbox: Click..!
The problems I'm facing are:
I don't know how to make that CSS animation effect of the letters coming and forming the text and scattering back as in the CodePen example.
I want to include the description part also which is in the description={data.desc}. Do I have to make the split again or any easy method to split both title and description together. I lag knowledge here.
My code is as below:
import React from "react";
export default function SlideCard(props) {
const { id, idx, title } = props;
function mainText() {
return (
<div style={{ border: "2px solid gold" }}>
<h1>{title}</h1>
</div>
);
}
//console.log(id);
function scatter() {
return (
<div>
{title.split("").map((item, index) => {
const style = {
position: "absolute",
top: Math.floor(Math.random() * 200) + "px",
left: Math.floor(Math.random() * 400) + "px",
zIndex: "initial",
color: "#AAA",
overflow: "hidden",
transition: "left 2s, top 2s, color 2s"
};
return (
<div key={index}>
<div className="scatter" style={style}>
{item}
</div>
</div>
);
})}
</div>
);
}
return (
<div>
<div>{idx === id && mainText()}</div>
{idx !== id && scatter()}
</div>
);
}
He's maintaining two same copies of each page data. One with class .position-data and one with .mutable. .position-data is hidden and only used for getting coordinates to bring back .mutable letters together.
Here is the simplified version of the CodePen example:
$(document).ready(function() {
assemble();
});
function scatter() {
for (var i = 0; i < 3; i++) {
var randLeft = Math.floor(Math.random() * $(window).width());
var randTop = Math.floor(Math.random() * $(window).height());
//randomly position .mutable elements
$(".mutable > span:eq(" + i + ")").animate({
left: randLeft,
top: randTop,
color: "#0005"
}, 2000, "easeInBack");
}
}
function assemble() {
for (var i = 0; i < 3; i++) {
$(".mutable > span:eq(" + i + ")").animate({
//get center position from .position data marked elements
left: $(".position-data > span:eq(" + i + ")").offset().left + "px",
top: $(".position-data > span:eq(" + i + ")").offset().top + "px",
color: "#000"
}, 2000, 'easeOutBounce');
}
}
span {
font-size: 30px;
}
.position-data {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
opacity: 0.4;
color: green;
}
.mutable span {
position: absolute;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<div class="position-data">
<span>a</span>
<span>b</span>
<span>c</span>
</div>
<div class="mutable shadowed">
<span>a</span>
<span>b</span>
<span>c</span>
</div>
<button onclick="scatter()">Scatter</button>
<button onclick="assemble()">Assemble</button>
I want to position the actively displaying Text container to the centre of the carousal.
The actively displaying text container is marked with .mutable class which has following css:
.mutable {
position: absolute;
overflow: hidden;
display: inline-block;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
So they all have full size of the document body. Using javascript function arrangeCurrentPage() in codepen they are set with coordinates such that they look centered.
I don't know how to make that transition effect of the letters coming and forming the text and scattering back as in the CodePen example.
Refer to above simplified code. Use the buttons to see the effects separately. All letter elements are put in a div with class .mutable. All letters are position: absolute; so they are free to move anywhere. In javascript function scatter() I am simply assigning top and left properties to random coordinates. With animation effect they scatter smoothly. In codepen he is achieving the animation effect using css transition transition: left 2s, top 2s, color 2s;
To bring them back simply us css selector nth-child .position-data > span:eq(n) to get corresponding coordinates of the character. Refer javascript function assemble().
I want to include the description part also which is in the description={data.desc}. Do I have to make the split again or any easy method to split both title and description together. I lag knowledge here.
As title and description will be displayed separately with separate presentation styles. Code will be cleaner if you keep split versions of them separately. You can create utility methods like split(text), scramble(array), arrange(array)and use it for both like cramble(titleArray)and scramble(descArray).
If we use 'position:relative' on letters we don't have to maintain two copies of same data:
<!DOCTYPE html>
<head>
<title>Document</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
<script>
$(document).ready(function() {
assemble();
});
function randomX() {
return Math.floor(Math.random() * $(window).width() * 0.7) - $(window).width() * 0.25;
}
function randomY() {
return Math.floor(Math.random() * $(window).height() * 0.7) - $(window).height() * 0.25;
}
function scatter() {
for (var i = 0; i < 3; i++) {
$(".mutable > span:eq(" + i + ")").animate({
left: randomX(),
top: randomY(),
color: "#f118"
}, 2000, "easeInBack");
}
}
function assemble() {
for (var i = 0; i < 3; i++) {
$(".mutable > span:eq(" + i + ")").animate({
left: '0px',
top: '0px',
color: "#000"
}, 2000, 'easeOutBounce');
}
}
</script>
<style>
span {
font-size: 30px;
}
.mutable {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.mutable span {
position: relative; /* <--- */
}
</style>
</head>
<body>
<div class="mutable">
<span>a</span>
<span>b</span>
<span>c</span>
</div>
<button onclick="scatter()">Scatter</button>
<button onclick="assemble()">Assemble</button>
</body>
</html>
The sandbox demo you want to emulate is using an animation they call Alphabet Soup. After a quick google search, I found an npm package that can help you implement this in your react component: https://github.com/OrigenStudio/react-alphabet-soup.
Why reinvent the wheel?
I have this modal, and would like to make many of them on the same page. I tried a lot, but nope. Any idea how to do this? Thanks.
Here the code:
HTML
<div class="containerofelements">
<div id="s2-01" class="resizeratio">1.00</div>
<div id="s-wrap" class="wrap" style="transform: scale(0.04);">
<div class="elements" style="transform: scale(0.04);">
<div id="s-rr"class="resizeratio">0.04</div>
<div id="s-001" class="slider16" midicc="1, 80" colour="#ff3300"></div>
</div>
</div>
</div>
CSS
.containerofelements {
}
.resizeratio {
display: inline-block;
cursor: pointer;
border-style: solid;
color: red;
width: 50px;
height: 20px;
}
.wrap {
background-color: rgba(0, 80, 128, 1);
transform-origin: 1880px 0;
}
.elements {
width: 1600px;
height: 800px;
transform-origin: 1880px 0;
}
JS
$(document).ready(function() {
$(".resizeratio").click(function() {
var el = $('.elements');
var scale = $(this).text();
el.css({
transform: "scale(" + scale + ")"
});
});
});
$(document).ready(function() {
$(".resizeratio").click(function() {
var el = $('.wrap');
var scale = $(this).text();
el.css({
transform: "scale(" + scale + ")"
});
});
});
I tried to apply separate names, IDs, but no good results without replicating everything which is not a good practice I suppose, a waste of code.
I'm building a little site with full page horizontal and vertical scrolling. Check out a codepen demo here. There is a bug with the demo, the 'left' and 'up' buttons don't work how they're supposed to. The 'right' and 'down' buttons work fine. I just threw that together to show you what I'm talking about (excuse my inline styling).
First off, I need to incorporate touchEvents to make the full page scrolling work on mobile devices. If the user swipes left, right, down, or up, the page should move accordingly. I'm still learning the fundamentals of JS and I have no idea where to start with that.
Secondly, I have a few doubts about whether or not I'm using best practices in my JS. For one thing, I repeat myself a lot. For another, I'm pretty sure there's a simpler method for what I'm trying to do. I'd appreciate it if you could take a look at my code and give me some suggestions. Thanks!
You need to modify these two in CSS:
#center.cslide-up {
top: 100vh;
}
#center.cslide-left {
left: 100vw;
}
First one: When the up button is clicked, it will move 100vh down from top position.
Second one: When the left button is clicked, it will move 100vw right from left position.
As far as for mobile phones, I'd suggest try using:
Hammer.js : https://hammerjs.github.io/
Or Refer this answer: https://stackoverflow.com/a/23230280/2474466
And you can reduce the lines of code by cooking up a function and calling it like this: (Make sure to declare panel2 variable globally)
btnL.addEventListener('click', function() {
swiper("left");
});
btnLBack.addEventListener('click', function() {
swiper("left");
});
function swiper(dir){
panelC.classList.toggle('cslide-'+dir);
if(dir=="up") panel2=panelU;
else if(dir=="right") panel2=panelR;
else if(dir=="left") panel2=panelL;
else if(dir=="down") panel2=panelD;
panel2.classList.toggle('slide-'+dir);
}
The function swiper takes a single argument dir which determines in which direction it has to be moved. And you can concatenate the dir with cslide- to move the center container. And use if/else conditions to determine which panel to move and use the same idea for it as well.
And to make it more simpler and a bit efficient, if you're not making use of any other eventlisteners for the buttons or panels and the only aim is to toggle the class around, you can just use inline onClick="swiper('direction');" attribute on the panels and buttons to trigger it only when needed instead of defining the eventlisteners in the script.
var panel2;
var panelC = document.getElementById('center');
var panelU = document.getElementById('up');
var panelR = document.getElementById('right');
var panelD = document.getElementById('down');
var panelL = document.getElementById('left');
var btnU = document.getElementById('btn-up');
var btnR = document.getElementById('btn-right');
var btnD = document.getElementById('btn-down');
var btnL = document.getElementById('btn-left');
var btnUBack = document.getElementById('btn-up-back');
var btnRBack = document.getElementById('btn-right-back');
var btnDBack = document.getElementById('btn-down-back');
var btnLBack = document.getElementById('btn-left-back');
btnU.addEventListener('click', function() {
swiper("up");
});
btnUBack.addEventListener('click', function() {
swiper("up");
});
btnR.addEventListener('click', function() {
swiper("right");
});
btnRBack.addEventListener('click', function() {
swiper("right");
});
btnD.addEventListener('click', function() {
swiper("down");
});
btnDBack.addEventListener('click', function() {
swiper("down");
});
btnL.addEventListener('click', function() {
swiper("left");
});
btnLBack.addEventListener('click', function() {
swiper("left");
});
function swiper(dir){
panelC.classList.toggle('cslide-'+dir);
if(dir=="up") panel2=panelU;
else if(dir=="right") panel2=panelR;
else if(dir=="left") panel2=panelL;
else if(dir=="down") panel2=panelD;
panel2.classList.toggle('slide-'+dir);
}
* {
margin: 0;
padding: 0;
transition: 1.5s ease;
-webkit-transition: 1.5s ease;
overflow: hidden;
background: white;
}
.panel {
width: 100vw;
height: 100vh;
display: block;
position: absolute;
border: 1px solid blue;
}
.btn {
position: absolute;
padding: 16px;
cursor: pointer;
}
#center {
top: 0px;
left: 0px;
right: 0px;
bottom: 0px;
}
#center.cslide-up {
top: 100vh;
}
#center.cslide-left {
left: 100vw;
}
#center.cslide-right {
left: -100vw;
}
#center.cslide-down {
top: -100vh;
}
#up {
top: -100vh;
}
#up.slide-up {
top: 0;
}
#right {
right: -100vw;
}
#right.slide-right {
right: 0;
}
#down {
bottom: -100vh;
}
#down.slide-down {
bottom: 0;
}
#left {
left: -100vw
}
#left.slide-left {
left: 0;
}
<div class="panel" id="center">
<div class="btn" id="btn-up" style="text-align: center; width: 100%;">
up
</div>
<div class="btn" id="btn-right" style="right: 0; top: 50%;">
right
</div>
<div class="btn" id="btn-down" style="text-align: center; bottom: 0; width: 100%;">
down
</div>
<div class="btn" id="btn-left" style="top: 50%;">
left
</div>
</div>
<div class="panel" id="up">
<div class="btn" id="btn-up-back" style="bottom: 0; width: 100%; text-align: center;">
back
</div>
</div>
<div class="panel" id="right">
<div class="btn" id="btn-right-back" style="left: 0; top: 50%;">
back
</div>
</div>
<div class="panel" id="down">
<div class="btn" id="btn-down-back" style="top: 0; width: 100%; text-align: center;">
back
</div>
</div>
<div class="panel" id="left">
<div class="btn" id="btn-left-back" style="right: 0; top: 50%;">
back
</div>
</div>
So I have a set of elements called .project-slide, one after the other. Some of these will have the .colour-change class, IF they do have this class they will change the background colour of the .background element when they come into view. This is what I've got so far: https://codepen.io/neal_fletcher/pen/eGmmvJ
But I'm looking to achieve something like this: http://studio.institute/clients/nike/
Scroll through the page to see the background change. So in my case what I'd want is that when a .colour-change was coming into view it would slowly animate the opacity in of the .background element, then slowly animate the opacity out as I scroll past it (animating on scroll that is).
Any suggestions on how I could achieve that would be greatly appreciated!
HTML:
<div class="project-slide fullscreen">
SLIDE ONE
</div>
<div class="project-slide fullscreen">
SLIDE TWO
</div>
<div class="project-slide fullscreen colour-change" data-bg="#EA8D02">
SLIDE THREE
</div>
<div class="project-slide fullscreen">
SLIDE TWO
</div>
<div class="project-slide fullscreen colour-change" data-bg="#cccccc">
SLIDE THREE
</div>
</div>
jQuery:
$(window).on('scroll', function () {
$('.project-slide').each(function() {
if ($(window).scrollTop() >= $(this).offset().top - ($(window).height() / 2)) {
if($(this).hasClass('colour-change')) {
var bgCol = $(this).attr('data-bg');
$('.background').css('background-color', bgCol);
} else {
}
} else {
}
});
});
Set some data-gb-color with RGB values like 255,0,0…
Calculate the currently tracked element in-viewport-height.
than get the 0..1 value of the inViewport element height and use it as the Alpha channel for the RGB color:
/**
* inViewport jQuery plugin by Roko C.B.
* http://stackoverflow.com/a/26831113/383904
* Returns a callback function with an argument holding
* the current amount of px an element is visible in viewport
* (The min returned value is 0 (element outside of viewport)
*/
;
(function($, win) {
$.fn.inViewport = function(cb) {
return this.each(function(i, el) {
function visPx() {
var elH = $(el).outerHeight(),
H = $(win).height(),
r = el.getBoundingClientRect(),
t = r.top,
b = r.bottom;
return cb.call(el, Math.max(0, t > 0 ? Math.min(elH, H - t) : (b < H ? b : H)), H);
}
visPx();
$(win).on("resize scroll", visPx);
});
};
}(jQuery, window));
// OK. Let's do it
var $wrap = $(".background");
$("[data-bg-color]").inViewport(function(px, winH) {
var opacity = (px - winH) / winH + 1;
if (opacity <= 0) return; // Ignore if value is 0
$wrap.css({background: "rgba(" + this.dataset.bgColor + ", " + opacity + ")"});
});
/*QuickReset*/*{margin:0;box-sizing:border-box;}html,body{height:100%;font:14px/1.4 sans-serif;}
.project-slide {
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.project-slide h2 {
font-weight: 100;
font-size: 10vw;
}
<div class="project-slides-wrap background">
<div class="project-slide">
<h2>when in trouble...</h2>
</div>
<div class="project-slide" data-bg-color="0,200,255">
<h2>real trouble...</h2>
</div>
<div class="project-slide">
<h2>ask...</h2>
</div>
<div class="project-slide" data-bg-color="244,128,36">
<h2>stack<b>overflow</b></h2>
</div>
</div>
<script src="//code.jquery.com/jquery-3.1.0.js"></script>
Looks like that effect is using two fixed divs so if you need something simple like that you can do it like this:
But if you need something more complicated use #Roko's answer.
var fixed = $(".fixed");
var fixed2 = $(".fixed2");
$( window ).scroll(function() {
var top = $( window ).scrollTop();
var opacity = (top)/300;
if( opacity > 1 )
opacity = 1;
fixed.css("opacity",opacity);
if( fixed.css('opacity') == 1 ) {
top = 0;
opacity = (top += $( window ).scrollTop()-400)/300;
if( opacity > 1 )
opacity = 1;
fixed2.css("opacity",opacity);
}
});
.fixed{
display: block;
width: 100%;
height: 200px;
background: blue;
position: fixed;
top: 0px;
left: 0px;
color: #FFF;
padding: 0px;
margin: 0px;
opacity: 0;
}
.fixed2{
display: block;
width: 100%;
height: 200px;
background: red;
position: fixed;
top: 0px;
left: 0px;
color: #FFF;
padding: 0px;
margin: 0px;
opacity: 0;
}
.container{
display: inline-block;
width: 100%;
height: 2000px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
Scroll me!!
</div>
<div class="fixed">
</div>
<div class="fixed2">
</div>
After studying, looking at tutorials, getting some help here, I almost got this script working as intended. However, I'm not at a stand still and my brain hurts trying to figure out the logic.
The problem is the script allows for over scrolling forward. How can I stop that?
jQuery:
var $item = $('.slider'),
start = 0,
view = $('#main-header').width(),
end = $('.slider').width();
$('.next').click(function () {
if (start < view) {
start++;
$item.animate({
'left': '-=100%'
});
}
});
$('.prev').click(function () {
if (start > 0) {
start--;
$item.animate({
'left': '+=100%'
});
}
});
HTML:
<div id="main-header">
<div class="slider">
<div class="item-post" style="background: url(http://4.bp.blogspot.com/-LjJWOy7K-Q0/VOUJbMJr0_I/AAAAAAAAdAg/I2V70xea8YE/s320-c/enviroment-5.jpg) center"></div>
<div class="item-post" style="background: url(http://1.bp.blogspot.com/-l3UnbspFvv0/VOUK8M-34UI/AAAAAAAAdA0/ooGyXrHdNcg/s320-c/enviroment-2.jpg)"></div>
<div class="item-post" style="background: url(http://2.bp.blogspot.com/-cun1kQ42IBs/VOUaSPfnebI/AAAAAAAAdBQ/yTEj9K-BGdk/s320-c/fashion-3.jpg)"></div>
</div>
<div class="prev"></div>
<div class="next"></div>
</div>
CSS:
#main-header {
overflow: hidden;
position: relative;
}
.slider {
width: 100%;
height: 200px;
position: relative;
}
.item-post {
width: 100%;
height: 200px;
background: rgba(0, 0, 0, 0.1);
background-size: cover !important;
background-position: center !important;
position: absolute;
top: 0;
}
.item-post:first-of-type {
left: 0;
}
.item-post:nth-of-type(2) {
left: 100%;
}
.item-post:last-of-type {
left: 200%;
}
.prev, .next {
position: absolute;
top: 0;
bottom: 0;
width: 25px;
background: rgba(0, 0, 0, 0.2);
cursor: pointer;
}
.prev {
left: 0;
}
.next {
right: 0;
}
jsfiddle: http://jsfiddle.net/51maaks8/8/
In order to determine whether there is another slide visible, you could create a function that adds the .offsetLeft value of the parent element to the .offsetLeft value of the last visible slide element and its width. You would then subtract the width of the parent element from the sum of these calculations.
In doing so, you are essentially calculating the position of the last slide element relative to the left positioning of the .item-wrapper parent element.
function moreVisibleSlides() {
var $last = $('#slider > .item-wrapper > .item-post:last:visible'),
positionRelativeToParent = $last.parent()[0].offsetLeft + $last[0].offsetLeft + $last.width() - $item.width();
return positionRelativeToParent > 5;
}
For the click event listener, only slide the element if there are more visible slides, which is determined by the boolean returned by the moreVisibleSlides function. In addition, I also added a check (!$item.is(':animated')) to prevent the next slide from being animated if there is currently an animation in progress. This ensures that you can't click the .next button multiple times during an animation and then over scroll regardless of whether or not there are more visible slides.
Updated Example
$('.next').click(function () {
if (moreVisibleSlides() && !$item.is(':animated')) {
start++;
$item.animate({
'left': '-=100%'
});
}
});