How to trigger TimelineMax animation when ScrollToPlugin is scrolling (ScrollMagic)? - gsap

I have a section with button which on click triggers scroll to the next section.
What I want to do is when this scroll event is happening I want to trigger my tl.from animations.
Right now animations tl.from are triggered only on user scroll but not on button pressed.
const button = document.getElementById('cta');
let tl = new TimelineMax({ onUpdate: updatePercentage })
function scrollToNextSection() {
TweenLite.to(window, 2, { scrollTo: '#section-1'});
}
tl.from('.section__left', .5, { x: 200, opacity: 0, ease: Power4.easeOut })
tl.from('.section__right', .5, { x: -200, opacity: 0, ease: Power4.easeOut })
tl.from('.section__img', 1, { x: 400, opacity: 0 })
// init controller
var controller = new ScrollMagic.Controller();
// create a scene
new ScrollMagic.Scene({
triggerElement: '#section-1',
triggerHook: 'onLeave',
duration: '100%',
})
.setPin("#section-1")
.setTween(tl)
.addTo(controller);
function updatePercentage() {
tl.progress();
}
button.addEventListener('click', scrollToNextSection);

Try to change your code like that :)
const button = document.getElementById('cta');
let tl = new TimelineMax({ onUpdate: updatePercentage }).from('.section__left', .5, { x: 200, opacity: 0, ease: Power4.easeOut })
.from('.section__right', .5, { x: -200, opacity: 0, ease: Power4.easeOut })
.from('.section__img', 1, { x: 400, opacity: 0 });
function scrollToNextSection() {
TweenLite.to(window, 2, { scrollTo: '#section-1'});
}
// init controller
var controller = new ScrollMagic.Controller();
// create a scene
new ScrollMagic.Scene({
triggerElement: '#section-1',
triggerHook: 'onLeave',
duration: '100%',
})
.setPin("#section-1")
.setTween(tl)
.addTo(controller);
function updatePercentage() {
tl.progress();
}

Related

how to create a barcode scanner with quagga with fixed scanning canvas

I'm on Nuxt 2.15.4 and using Quagga2. I want to create a scanner like mobile scanners where the camera is full screen and there is a blue rectangle that the barcode must be placed inside of, and after scanning there will be a red line through the barcode canvas. With Quagga.js I can manipulate the CSS and area property in canvas but it cause 2 problems:
The actual scanning zone will be messed up and I don't know where the scanning exactly happens.
The successful red line-through style will be messed up.
Also, after a successful scan I need the red line-through to remain and not disappear and no scanning happen for a period of time (so I can decide to continue scanning or do something with result).
Here is the code I implemented on in app (also need help on activating torch and zoom):
<template>
<section id="container" class="container">
<div id="interactive" class="viewport"></div>
{{code}}
</section>
</template>
<script>
import Quagga from '#ericblade/quagga2';
export default {
data() {
return {
code:[]
};
},
methods:{
async checkDevice() {
let md = navigator.mediaDevices;
if (!md || !md.enumerateDevices) return false;
const devices = await navigator.mediaDevices.enumerateDevices();
return devices.some((device) => "videoinput" === device.kind);
},
initQuagga(){
Quagga.init({
inputStream : {
name : "Live",
type : "LiveStream",
area: {
top: "0%",
right: "0%",
left: "0%",
bottom: "0%",
},
},
locate: false,
decoder : {
readers : [
"ean_reader",
],
}
},(err)=>{
if(err){
return
}
// this.checkCapabilities();
this.startQuagga()
});
},
// checkCapabilities(){
// var track = Quagga.CameraAccess.getActiveTrack();
// var capabilities = {};
// if (typeof track.getCapabilities === 'function') {
// capabilities = track.getCapabilities();
// }
// this.applySettingsVisibility('zoom', capabilities.zoom);
// this.applySettingsVisibility('torch', capabilities.torch);
// },
// applySetting: function(setting, value) {
// var track = Quagga.CameraAccess.getActiveTrack();
// if (track && typeof track.getCapabilities === 'function') {
// switch (setting) {
// case 'zoom':
// return track.applyConstraints({advanced: [{zoom: parseFloat(value)}]});
// case 'torch':
// return track.applyConstraints({advanced: [{torch: !!value}]});
// }
// }
// },
startQuagga(){
Quagga.start()
Quagga.onProcessed(function (result) {
let drawingCtx = Quagga.canvas.ctx.overlay,
drawingCanvas = Quagga.canvas.dom.overlay;
if (result) {
if (result.boxes) {
drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
result.boxes.filter(function (box) {
return box !== result.box;
}).forEach(function (box) {
Quagga.ImageDebug.drawPath(box, {x: 0, y: 1}, drawingCtx, {color: "#00F", lineWidth: 2});
});
}
if (result.box) {
Quagga.ImageDebug.drawPath(result.box, {x: 0, y: 1}, drawingCtx, {color: "#008", lineWidth: 2});
}
if (result.codeResult && result.codeResult.code) {
Quagga.ImageDebug.drawPath(result.line, {x: 'x', y: 'y'}, drawingCtx, {color: 'red', lineWidth: 3});
}
}
})
Quagga.onDetected(this.onDetected);
},
onDetected(data) {
let barCodeData = data.codeResult.code;
console.log(barCodeData);
},
},
async mounted(){
let data = await this.checkDevice();
if (data) {
this.initQuagga();
}
},
beforeDestroy(){
Quagga.stop()
}
}
</script>
<style lang="scss">
#container {
width: 640px;
margin: 20px auto;
padding: 10px;
}
#interactive.viewport {
width: 640px;
height: 480px;
}
#interactive.viewport canvas, video {
float: left;
width: 640px;
height: 480px;
}
#interactive.viewport canvas.drawingBuffer, video.drawingBuffer {
margin-left: -640px;
}
</style>

draw rectangle with mouse move with Konva in VUE

This is the behavior I want to achieve in Vue.js Here is the Js fiddle example i am trying to make: https://jsfiddle.net/richardcwc/ukqhf54k/
//Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
//Variables
var canvasx = $(canvas).offset().left;
var canvasy = $(canvas).offset().top;
var last_mousex = last_mousey = 0;
var mousex = mousey = 0;
var mousedown = false;
//Mousedown
$(canvas).on('mousedown', function(e) {
last_mousex = parseInt(e.clientX-canvasx);
last_mousey = parseInt(e.clientY-canvasy);
mousedown = true;
});
//Mouseup
$(canvas).on('mouseup', function(e) {
mousedown = false;
});
//Mousemove
$(canvas).on('mousemove', function(e) {
mousex = parseInt(e.clientX-canvasx);
mousey = parseInt(e.clientY-canvasy);
if(mousedown) {
ctx.clearRect(0,0,canvas.width,canvas.height); //clear canvas
ctx.beginPath();
var width = mousex-last_mousex;
var height = mousey-last_mousey;
ctx.rect(last_mousex,last_mousey,width,height);
ctx.strokeStyle = 'black';
ctx.lineWidth = 10;
ctx.stroke();
}
//Output
$('#output').html('current: '+mousex+', '+mousey+'<br/>last: '+last_mousex+', '+last_mousey+'<br/>mousedown: '+mousedown);
});
I am using a library called Konva.js. Right now I am able to free drawing in Vue.js with Konva.js. But When I try to draw the rectangle with mousemove. It does not work correctly. I am not sure what causes the issue. Thanks for any help! Here is my work on
Code sandbox
This is the behavior I found out for my work. It only draws the rectangle after the mouse move event and then mouse click event.
<template>
<v-stage
ref="stage"
:config="stageSize"
#mousemove="handleMouseMove"
#mouseDown="handleMouseDown"
#mouseUp="handleMouseUp"
>
<v-layer ref="layer">
<v-text
ref="text"
:config="{
x: 10,
y: 10,
fontSize: 20,
text: text,
fill: 'black',
}"
/>
<v-rect
v-for="(rec, index) in recs"
:key="index"
:config="{
x: Math.min(rec.startPointX, rec.startPointX + rec.width),
y: Math.min(rec.startPointY, rec.startPointY + rec.height),
width: Math.abs(rec.width),
height: Math.abs(rec.height),
fill: 'rgb(0,0,0,0)',
stroke: 'black',
strokeWidth: 3,
}"
/>
</v-layer>
</v-stage>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
stageSize: {
width: width,
height: height,
},
text: "Try to draw a rectangle",
lines: [],
isDrawing: false,
recs: [],
};
},
methods: {
handleMouseDown(event) {
this.isDrawing = true;
const pos = this.$refs.stage.getNode().getPointerPosition();
this.setRecs([
...this.recs,
{ startPointX: pos.x, startPointY: pos.y, width: 0, height: 0 },
]);
},
handleMouseUp() {
this.isDrawing = false;
},
setRecs(element) {
this.recs = element;
},
handleMouseMove(event) {
// no drawing - skipping
if (!this.isDrawing) {
return;
}
// console.log(event);
const point = this.$refs.stage.getNode().getPointerPosition();
// handle rectangle part
let curRec = this.recs[this.recs.length - 1];
curRec.width = point.x - curRec.startPointX;
curRec.height = point.y - curRec.startPointY;
},
},
};
</script>
Demo: https://codesandbox.io/s/vue-konva-drawings-rectangles-ivjtu?file=/src/App.vue

Refreshing Konva shape state in Vue component

After dragging and releasing a shape I want it to snap to a close by position. To test this I create a shape at {x:100, y:100}, then drag it and it does snap to 0,0, but only the first time Im dragging it. Next time it will ignore me setting x,y.
I might be missing something basic here? Maybe I am not mutating store the right way. In the below code you can see three attempts to set x and y in handleDragend.
<template>
<div>
<v-stage
ref="stage"
:config="configKonva"
#dragstart="handleDragstart"
#dragend="handleDragend"
>
<v-layer ref="layer">
<v-regular-polygon
v-for="item in list"
:key="item.id"
:config="{
x: item.x,
y: item.y,
sides: 6,
rotation: item.rotation,
id: item.id,
radius: 50,
outerRadius: 50,
fill: 'green',
draggable: true,
}"
></v-regular-polygon>
</v-layer>
</v-stage>
</div>
</template>
<script>
const width = window.innerWidth;
const height = window.innerHeight;
export default {
data() {
return {
list: [],
dragItemId: null,
configKonva: {
width: width,
height: height,
}
};
},
methods: {
handleDragstart(e) {
//
},
handleDragend(e) {
let item = this.list.find(i => i.id === e.target.id());
let snapTo = { x: 0, y: 0}
// Attempt 1
Vue.set(this.list, 0, {
...item,
x: snapTo.x,
y: snapTo.y,
})
// Attempt 2
this.list = this.list.map(function(shape) {
if(shape.id === item.id) {
return {
...item,
x: snapTo.x,
y: snapTo.y,
}
}
})
},
},
mounted() {
this.list.push({
id: 1,
x: 100,
y: 100,
});
}
};
</script>
vue-konva updates nodes ONLY when you have changes in your template.
On the first snap, the coordinated in the template (and store) are changed from {100, 100} to {0, 0}.
When you drag the node the second time, the store still keeps {0, 0} in memory. So no changes are triggered and the node is not moved back.
There are two ways to solve the issue:
(1) Update Konva node position manually
handleDragend(e) {
let item = this.list.find(i => i.id === e.target.id());
let snapTo = { x: 0, y: 0 };
e.target.position(snapTo);
e.target.getLayer().batchDraw();
Vue.set(this.list, 0, {
...item,
x: snapTo.x,
y: snapTo.y
});
}
(2) Keep the store in sync with node position
You may need to register all position changes into the store:
handleDragMove(e) {
// do this on every "dragmove"
let item = this.list.find(i => i.id === e.target.id());
Vue.set(this.list, 0, {
...item,
x: e.target.x(),
y: e.target.y()
});
}
Demo: https://codesandbox.io/s/nifty-northcutt-v52ue

ScrollMagic - always animate first item first

Is there a way to ensure that the first item that appears always scrolls in first - using this code? At the moment it fires depending on the order of the elements. I am asking because the order of the elements changes throughout the site - I would rather not create a new scene for every different scenario. Someone might see a clever way of doing this. Thanks for any help in advance.
$('.data-scrollIn').each(function(index, elem) {
// Init ScrollMagic Controller
var controller = new ScrollMagic.Controller();
// Create Animations
h2 = $(elem).find('h2');
h3 = $(elem).find('h3');
btn = $(elem).find('.btn');
p = $(elem).find('p');
img = $(elem).find('img');
span = $(elem).find('span.js-fade-order');
var tl = new TimelineMax({
pause: true
});
tl.add("start") // add timeline label
.fromTo(span, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 0.3, "start")
.fromTo(h2, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 0.6, "start")
.fromTo(img, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 0.9, "start")
.fromTo(h3, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 1.2, "start")
.fromTo(p, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 1.5, "start")
.fromTo(btn, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 1.8, "start")
// Create the Scene and trigger when visible
var sceneFade = new ScrollMagic.Scene({
triggerElement: elem,
offset: -100 /* offset the trigger Npx below scene's top */
})
.setTween(tl)
.addTo(controller);
});
$('.data-scrollIn').each(function(index, elem) {
// Init ScrollMagic Controller
var controller = new ScrollMagic.Controller();
// Create Animations
fadeElem = $(elem).find('h2, h3, .btn, p, img, span.js-fade-order ');
var tl = new TimelineMax({
pause: true
});
tl.add("start") // add timeline label
.staggerFromTo(fadeElem, 0.6, {
opacity: 0,
y: "40px",
}, {
opacity: 1,
y: "0",
ease: Power0.easeInOut
}, 0.6, "start")
// Create the Scene and trigger when visible
var sceneFade = new ScrollMagic.Scene({
triggerElement: elem,
offset: -100 /* offset the trigger Npx below scene's top */
})
.setTween(tl)
.addTo(controller);
});

scrollmagic anchoring or pausing animation until user scrolls again

with the following code,
var firstScene = new TimelineLite()
.to(".some-background", 1000, { transform: 'translate(350px, 0)' })
.to(".old-text", 1000, { opacity: 0 })
.to(".new-text", 1000, { opacity: 1 })
.to(".new-text", 1000, { opacity: 0 })
.to(".new-text-2", 1000, { opacity: 1 })
new ScrollMagic.Scene({triggerHook: 'onLeave', triggerElement: "#container", duration: 5000})
.setPin("#container")
.setTween(firstScene)
.addTo(controller);
is it possible to 'anchor' or stop/pause the animation between fading in .new-text and fading it out until user scrolls again rather than creating another trigger somewhere to just delay the animation?