Greensock - Multiple, consequent tweens on a single object - javascript

I animate a div's position, then back to it's origin with a different ease:
var anim_a = new TimelineMax().to($(".mybox"), 3, {x: 400}).stop();
var anim_b = new TimelineMax().to($(".mybox"), 1, {x: 0, ease:Bounce.easeOut}).stop();
Problem is after both the animations ran once, they wont run anymore, why? and how can I repeat them on demand?
JSFIDDLE

var anim_a = function() {
var anm = new TimelineMax().to($(".mybox"), 1, {
x: 400
}).stop();
anm.play();
}
var anim_b = function() {
var anm = new TimelineMax().to($(".mybox"), 1, {
x: 0,
ease: Bounce.easeOut
}).stop();
anm.play();
}
$(".run-a").click(function(e) {
anim_a();
});
$(".run-b").click(function(e) {
anim_b();
});
.run-a,
.run-b {
background: green;
display: inline-block;
padding: 10px;
cursor: pointer;
}
.mybox {
width: 200px;
height: 200px;
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.2/TweenMax.min.js"></script>
<div class="run-a">Go</div>
<div class="run-b">Go back</div>
<div class="mybox"></div>
i am not used to this TimeLineMax. Still i made a small change to the code and now its working as said. Please check and confirm whether this will fit for your context. I just made anim_a and anim_b as two functions with play operation included.

Related

Adding Text on the selected canvas in Fabric js

I have fabric js multiple canvases and I would like to add Text on the selected canvas, instead of the last item of the array.
If a user creates multiple canvases then I need an option to add the text on the selected canvas.
Please run the code snippet or see the codepen demo of the current approach...
Thank you!
//================== Create Canvas start =================
var createCanvas = document.getElementById("createCanvas");
var canvasInstances = [];
createCanvas.addEventListener('click',function(){
var canvasContainer = document.getElementById("canvasContainer");
var newcanvas = document.createElement("canvas");
//newcanvas.classList.add("active");
canvasContainer.append(newcanvas);
var fabricCanvasObj = new fabric.Canvas(newcanvas, {
height: 400,
width: 400,
backgroundColor: '#ffffff',
});
canvasInstances.push(fabricCanvasObj);
console.log(canvasInstances);
})
//================== Create Canvas End =================
//================== Add Text ================
var addText = document.getElementById("addText");
addText.addEventListener('click',function(){
canvasInstances.forEach(function(current,id,array){
if(id === array.length - 1){
const converText = new fabric.IText(`Type Text`,{
type: 'text',
width: 200,
fontSize: 20,
left: 20,
top: 20,
fill: '#444'
});
current.add(converText);
current.renderAll();
return false;
}
})
})
//================== Add End =================
*{
box-sizing: border-box;
}
body{
background:#ccc;
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 200px;
}
.canvas-container{
margin-bottom:10px;
}
.add-text{
margin: 20px;
width: 400px;
text-align: center;
border:1px dashed red;
padding: 15px;
cursor:pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.5.0/fabric.min.js"></script>
<div class="add-text" id="addText">Add Text</div>
<div id="canvasContainer">
</div>
<button id="createCanvas">Create Canvas</button>
Declare canvasobject globally, you can try below code
<script type="text/javascript">
var canvasobject;
window.onload = function () {
canvasobject = new fabric.Canvas('myCanvas');
canvasobject.backgroundColor = '#0056d6';
canvasobject.renderAll();
//
function addtext() {
var textvalue = "Type here";
var fontcolor = "#333";
var fontFamily = "Baloo 2";
var myfont = new FontFaceObserver(fontFamily);
myfont.load()
.then(function () {
// when font is loaded, use it.
var text = new fabric.Text(textvalue, {
left: 100,
top: 100,
fontFamily: fontFamily,
fill: fontcolor,
});
canvasobject.add(text);
canvasobject.renderAll();
}).catch(function (e) {
//console.log(e);
console.log('font loading failed ' + fontFamily);
});
}
}

How do I get a smooth interpolation between keyframes?

I am trying to make an animation system for my three.js project. I have a json file for the information. I was able to make the animation play. But, at the moment they need to be somewhere, they move to that location, instead of slowly moving to that location over time. The json file tells the program where the specific object needs to be at a certain location. For example:
Json File:
{
"right": {
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 1, 0],
"0.5": [1, 1, 0]
}
}
Json files tells you position, at what second, then the positions.
Code:
for (const [key, value] of Object.entries(json.position.right))
if(seconds === json.position.right[key]) {
obj.position.x = json.right.position[key][0];
obj.position.y = json.right.position[key][1];
obj.position.z = json.right.position[key][2];
}
}
In the code, I loop through the json file's right cube position (which tells when the position changes happen). If the seconds match, it moves to that position.
How would I be able to get the movement inbetween the keyframes for the object?
Here is the example: https://mixed-polyester-a.glitch.me/
Here is all the code: https://glitch.com/edit/#!/mixed-polyester-a
I used Blockbench to export models as .OBJ files, materials as .MTL files, and animations as .JSON files.
Sorry if it sounds confusing, didn't really know how to explain it. Any help would be highly appreciated.
Three.js has a method called MathUtils.lerp() that takes in a starting position, an ending position, and an interpolation value between [0, 1]. You could use this to tween your object's current position to its target destination on each frame, as demonstrated in the example below:
const container = document.getElementById("container");
const ball = document.getElementById("ball");
// Set current positions
let ballPosX = 0;
let ballPosY = 0;
// Set target positions
let ballTargetX = 0;
let ballTargetY = 0;
// Update target positions on click
function onClick(event) {
ballTargetX = event.layerX;
ballTargetY = event.layerY;
//console.log(event);
}
function update() {
// Interpolate current position towards targets
ballPosX = THREE.MathUtils.lerp(ballPosX, ballTargetX, 0.1);
ballPosY = THREE.MathUtils.lerp(ballPosY, ballTargetY, 0.1);
// Apply current position to our object
ball.style.left = ballPosX + "px";
ball.style.top = ballPosY + "px";
requestAnimationFrame(update);
}
container.addEventListener("click", onClick);
update();
#container {
width: 300px;
height: 300px;
background: #ddd;
}
#ball {
width: 10px;
height: 10px;
position: absolute;
top: 0;
left: 0;
background: #f90;
border-radius: 10px;
margin-top: -5px;
margin-left: -5px;
}
<div id="container">
<div id="ball"></div>
</div>
<script src="https://threejs.org/build/three.js"></script>
Update:
To create a linear timeline with keyframes, I've used gsap.to() with the keyframes parameter to feed all the positions to the timeline. See here and look up "keyframes" for more details. You can see it in action in the code demo below, you'll need to iterate through your JSON to feed that data to GSAP on your own, though. Good luck!
// Set position vector
const ballPos = {x: 0, y: 0};
const positions = {
"0.0": [0, 0],
"0.25": [0, 100],
"0.5": [100, 100],
"0.75": [100, 0],
"1.0": [0, 0],
}
const timeline = gsap.to(ballPos, {keyframes: [
{x: positions["0.0"][0], y: positions["0.0"][1], duration: 0.0},
{x: positions["0.25"][0], y: positions["0.25"][1], duration: 0.25},
{x: positions["0.5"][0], y: positions["0.5"][1], duration: 0.25},
{x: positions["0.75"][0], y: positions["0.75"][1], duration: 0.25},
{x: positions["1.0"][0], y: positions["1.0"][1], duration: 0.25},
]});
const container = document.getElementById("container");
const ball = document.getElementById("ball");
let timelineTime = 0;
function update() {
timelineTime += 0.001;
timelineTime %= 1;
timeline.seek(timelineTime);
// Apply current position to our object
ball.style.left = ballPos.x + "px";
ball.style.top = ballPos.y + "px";
requestAnimationFrame(update);
}
update();
#container {
width: 300px;
height: 300px;
background: #ddd;
}
#ball {
width: 10px;
height: 10px;
position: absolute;
top: 0;
left: 0;
background: #f90;
border-radius: 10px;
margin-top: -5px;
margin-left: -5px;
}
<div id="container">
<div id="ball"></div>
</div>
<script src="https://threejs.org/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.7.1/gsap.min.js"></script>

Link Background Color to Scroll Position

I want to link the background color of the body element to the scroll position such that when the page is scrolled all the way to the top its color 1, but then but then when its scrolled past screen.height, its a completely different color, but I want it to be interpolated such that when it is half-way scrolled, the color is only half-way transitioned. So far, I have it linked to
$(window).scrollTop() > screen.height
and
$(window).scrollTop() < screen.height
to add and remove a class that changes background-color but I want it to be dependent on scroll position not just to trigger the event, but rather smoothly animate it so fast scrolling transitions quickly, slow scrolling transitions it slowly.
One of possible solutions is to bind a rgb color to current height, count the step and set new rgb color depending on current position of scrolling. Here I've created the simplest case - black and white transition:
const step = 255 / $('#wrapper').height();
const multiplier = Math.round(
$('#wrapper').height() /
$('#wrapper').parent().height()
);
$('body').scroll(() => {
const currentStyle = $('body').css('backgroundColor');
const rgbValues = currentStyle.substring(
currentStyle.lastIndexOf("(") + 1,
currentStyle.lastIndexOf(")")
);
const scrolled = $('body').scrollTop();
const newValue = step * scrolled * multiplier;
$('#wrapper').css('background-color', `rgb(${newValue}, ${newValue}, ${newValue})`);
});
html,
body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
background-color: rgb(0, 0, 0);
}
#wrapper {
height: 200%;
width: 100%;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<section id="wrapper"></section>
And here is another one example with transition from yellow to blue:
const step = 255 / $('#wrapper').height();
const multiplier = Math.round(
$('#wrapper').height() /
$('#wrapper').parent().height()
);
$('body').scroll(() => {
const currentStyle = $('body').css('backgroundColor');
const rgbValues = currentStyle.substring(
currentStyle.lastIndexOf("(") + 1,
currentStyle.lastIndexOf(")")
);
const scrolled = $('body').scrollTop();
const newValue = step * scrolled * multiplier;
$('#wrapper').css('background-color', `rgb(${255 - newValue}, ${255 - newValue}, ${newValue})`);
});
html,
body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
overflow-x: hidden;
background-color: rgb(255, 255, 0);
}
#wrapper {
height: 200%;
width: 100%;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<section id="wrapper"></section>
var randomHex = function () {
return (parseInt(Math.random()*16)).toString(16) || '0';
};
var randomColor = function () {
return '#'+randomHex()+randomHex()+randomHex();
};
var randomGradient = function () {
$('.longContent').css('background', 'linear-gradient(0.5turn, #222, '+randomColor()+','+randomColor()+')');
};
$(window).on('load', randomGradient);
body {
margin: 0;
}
.longContent {
height: 400vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tween.js/17.2.0/Tween.min.js"></script>
<div class="longContent"></div>
A much, much easier way to accomplish what you're looking to do is by simply using a gradient as the background.
There is absolutely zero need for any JS here, which will only slow down the page.
body {
height: 600vh;
background: linear-gradient(#2E0854, #EE3B3B)
}
Is there a particular reason you want to do this with JS?

freewall.js plugin works only after resize

So, I'm testing this awesome jquery plugin called freewall.js.
It seems to work absolutely fine, however, only after resizing the page.
On first load, the blocks appear too close to each other in a really messed up fashion. A little resize on the page and voilá! it's perfect.
This is the code i'm using (provided by the example file, which seems to work just fine on their server):
<script type="text/javascript">
var temp = "<div class='brick' style='width:{width}px;'><img src='{index}.gif' width='100%'></div>";
var w = 1, h = 1, html = '', limitItem = 10;
for (var i = 0; i < limitItem; ++i) {
w = 1 + 3 * Math.random() << 0;
html += temp.replace(/\{width\}/g, w*150).replace("{index}", i + 1);
}
$("#freewall").html(html);
var wall = new Freewall("#freewall");
wall.reset({
selector: '.brick',
animate: true,
gutterY: 15,
gutterX: 15,
cellW: 150,
cellH: 'auto',
onResize: function() {
wall.fitWidth();
}
});
var images = wall.container.find('.brick');
images.find('img').load(function() {
wall.fitWidth();
});
</script>
Here's the css style:
<style type="text/css">
body {
background-color: #557f9d;
font-size: 13pt;
font-family: 'Source Sans Pro', sans-serif;
font-size: 15pt;
font-weight: 300 !important;
color: #fff;
letter-spacing: -0.025em;
line-height: 1.75em;
}
.free-wall {
margin: 0px;
}
.brick img {
margin: 0;
display: block;
}
</style>
And here's the page in question:
Example page
Thanks in advance!
Try this :
$(document).ready(function() {
$(document).resize();
});
EDIT: The code is simple, once the document is ready, it triggers the code that is bound to the resize handler, in your case the whole wall thingy is tied to it. So the first thing that the javascript do once everything is up and running is call the code and the plugin does its wonder in the back. Glad I could help.

mo.js animation for multiple items

I need your help.
To apply animation to multiple elements with a single class of "my-button"? Now this only works for a single button.
Replacement querySelector on querySelectorAll not solve the problem, the script becomes not working
Thank you.
var el = document.querySelector('.my-button'),
elSpan = el.querySelector('span'),
// mo.js timeline obj
timeline = new mojs.Timeline(),
scaleCurve = mojs.easing.path('M0,100 L25,99.9999983 C26.2328835,75.0708847 19.7847843,0 100,0'),
// tweens for the animation:
// burst animation
tween1 = new mojs.Burst({
parent: el,
duration: 1500,
shape : 'circle',
fill : [ '#e67e22', '#DE8AA0', '#8AAEDE', '#8ADEAD', '#DEC58A', '#8AD1DE' ],
x: '50%',
y: '50%',
opacity: 0.8,
childOptions: { radius: {20:0} },
radius: {40:120},
angle: {0: 180},
count: 8,
isSwirl: true,
isRunLess: true,
easing: mojs.easing.bezier(0.1, 1, 0.3, 1)
}),
// ring animation
tween2 = new mojs.Transit({
parent: el,
duration: 750,
type: 'circle',
radius: {0: 50},
fill: 'transparent',
stroke: '#2ecc71',
strokeWidth: {15:0},
opacity: 0.6,
x: '50%',
y: '50%',
isRunLess: true,
easing: mojs.easing.bezier(0, 1, 0.5, 1)
}),
// icon scale animation
tween3 = new mojs.Tween({
duration : 900,
onUpdate: function(progress) {
if(progress > 0.3) {
var scaleProgress = scaleCurve(progress);
elSpan.style.WebkitTransform = elSpan.style.transform = 'scale3d(' + scaleProgress + ',' + scaleProgress + ',1)';
elSpan.style.WebkitTransform = elSpan.style.color = '#2ecc71';
} else {
elSpan.style.WebkitTransform = elSpan.style.transform = 'scale3d(0,0,1)';
elSpan.style.WebkitTransform = elSpan.style.color = 'rgba(0,0,0,0.3)';
}
}
});
// add tweens to timeline:
timeline.add(tween1, tween2, tween3);
// when clicking the button start the timeline/animation:
el.addEventListener('mousedown', function() {
timeline.start();
});
.wrapper {
display: flex;
justify-content: center;
align-items: center;
align-content: center;
text-align: center;
height: 200px;
}
.my-button {
background: transparent;
border: none;
outline: none;
margin: 0;
padding: 0;
position: relative;
text-align:center;
}
svg {
top: 0;
left: 0;
}
.send-icon {
position: relative;
font-size: 40px;
color: rgba(0,0,0,0.3);
}
<link href="http://fontawesome.io/assets/font-awesome/css/font-awesome.css" rel="stylesheet"/>
<script src="http://netgon.net/mo.min.js"></script>
<div class="wrapper">
<button class="my-button">
<span class="send-icon fa fa-paper-plane"></span>
</button>
</div>
Codepen
Working CodePen Here!
the "parent element" el must be a single element, so achieving this would be tricky and create a complicated messy code.
so I created a different method that'll create the animations once and set their position using tune() when a button is clicked, this will improve performance since you only have one animation object in the DOM.
instead of creating three animations for each parent/button.
I listen to the all buttons with the class "my-button" set animation's top and left values accordingly
$( ".my-button" ).on( "click", function() {
aniPos = findCenter($(this));
myAnimation1.tune({ top: aniPos.y, left: aniPos.x });
myAnimation2.tune({ top: aniPos.y, left: aniPos.x });
myTimeline.replay();
anipos is calculated with a simple function that'll return an object with .x and .y values
function findCenter ($this) {
var offset = $this.offset();
var width = $this.width();
var height = $this.height();
IDs = {
x: offset.left + (width / 2),
y: offset.top + (height / 2)
};
console.log(offset);
return IDs;
}
I also added a method to animate the clicked button automatically.
elSpan = this.querySelector('span');
new mojs.Tween({
duration : 900,
onUpdate: function(progress) {
if(progress > 0.3) {
var scaleProgress = scaleCurve(progress);
elSpan.style.WebkitTransform = elSpan.style.transform = 'scale3d(' + scaleProgress + ',' + scaleProgress + ',1)';
elSpan.style.WebkitTransform = elSpan.style.color = '#2ecc71';
} else {
elSpan.style.WebkitTransform = elSpan.style.transform = 'scale3d(0,0,1)';
elSpan.style.WebkitTransform = elSpan.style.color = 'rgba(0,0,0,0.3)';
}
}
}).play();
});
please note that my example will require JQuery to work.
I used the exact animations you provided but you can change them as long as you don't change X, Y, top and left properties

Categories