ScrollMagic - set a variable scene - gsap

I have a page with multiple similar sections with the class ".imgPanel" that I would like to apply the same animation to each section every time the page scrolls into view.
I don't want to create a new scene for each animation as they are all the same. I know there is a way to create a variable scene (i hope that is the correct terminology), and I am hoping someone can help.
Does anyone know how I would adjust the code below to make that happen. I am sure if I get one example of it with my code below I will be able to understand and use this technique again.
Thanks in advance. Adam.
function scrollMagic() {
if (!Modernizr.touch) {
var controller = new ScrollMagic.Controller({
duration: "200%",
});
var panelAnim = new TimelineMax();
panelAnim
.from($('.imgPanel'), 1.4, {
y: '-50px',
autoAlpha: 0,
ease: Power1.easeOut
}, '-=0.2')
var introScene = new ScrollMagic.Scene({
duration: "100%",
})
.setTween(panelAnim)
.addTo(controller);
}
}

I figured it out after watching Ihatetomatoes video
function scrollMagic() {
if (!Modernizr.touch) {
var controller = new ScrollMagic.Controller({
duration: "100%",
});
$('.imgPanel').each(function(){
var tween = TweenMax.from($(this), 1.4, { y: '-50px', autoAlpha: 0,
ease: Power1.easeOut }, '-=0.2');
var scene = new ScrollMagic.Scene ({
duration: "100%",
offset: -300, // offset trigger position by 100px
triggerElement: this
})
.setTween(tween)
.addTo(controller);
});
}
}

Related

Why variable value inside of canvas function not incrementing?

Here is an open GitHub issue Github Issue
Here is a Expo Snack
For some reason, variables are not incrementing inside the canvas function while outside works just fine. Please have a look at my code:
function home ({ navigation }) {
const [counter, setCounter] = useState(330);
useEffect(() => {
const timeout = setTimeout(() => {
setCounter(counter + 1);
}, 1000);
return () => {
clearTimeout(timeout);
};
}, [counter]);
console.log('outside ', counter);
const _onGLContextCreate = (gl) => {
var ctx = new Expo2DContext(gl);
// setInterval(() => {
// console.log('set interval doesnt refresh too ', counter);
// }, 1000);
console.log('inside ', counter);
let circle = {
x: counter,
y: 100,
radius: 30,
color: 'black'
}
let circle2 = {
x: 400,
y: 100,
radius: 30,
color: 'blue'
}
function drawCircle() {
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
ctx.fillStyle = circle.color;
ctx.fill();
ctx.closePath();
}
function drawCircle2() {
ctx.beginPath();
ctx.arc(circle2.x, circle2.y, circle2.radius, 0, Math.PI * 2);
ctx.fillStyle = circle2.color;
ctx.fill();
}
function update() {
drawCircle();
drawCircle2();
}
function animate() {
ctx.clearRect(0, 0, ctx.width, ctx.height);
requestAnimationFrame(animate);
update();
ctx.flush();
}
animate();
ctx.stroke();
ctx.flush();
};
return (
<GLView style={{ flex: 1 }} onContextCreate={_onGLContextCreate} />
);
}
export { home };
Here is what logs show:
outside 330
inside 330
outside 331
outside 332
outside 333
outside 334
outside 335
outside 336
outside 337
Does anybody know why is being read once in canvas and what could be the solution to increment it as in ouside in canvas function?
I don't know exactly what is the cause, but I found issues in the architecture, and when I fixed them it worked.
TL;DR see result here https://snack.expo.dev/#dozsolti/expogl-ball
You don't need to rerender the component because you update only the canvas so the useState will be swapped with useRef. Also, you probably meant to update the counter every second so for that I changed useTimeout with useInterval. (The useTimeout worked only because it was in a useEffect with the counter dependency which was updated, sort of like calling himself. The correct way was to use a useEffect when the component was loaded and a setInterval)
After that you needed to swap counter with counter.current
Keep in mind that the _onGLContextCreate is running only once so the circle and circle2 objects aren't changing. That's we I changed changed the x value in the update
Besides those, everything looks fine, I guess you optimize the code a little bit, like create a single DrawCircle function that takes x,y as parameters, and so on.

Adjust Transparency on ArcGIS Image Layer

I'm trying to adjust the transparency of a PNG that I am overlaying on ArcGIS/ESRI API for Javascript.
I have tried to set an opacity option in the var but, am not having much luck.
map.on("load", function() {
// create and add the layer
var mil = new esri.layers.MapImageLayer({
});
// create an add the actual image
var mi = new esri.layers.MapImage({
'extent': { 'xmin': -125.0, 'ymin': 22.0, 'xmax': -66.5, 'ymax': 52.0},
'href': 'https://some.website/some.image.PNG',
'opacity': 0.75
});
What am I doing wrong?
I added opacity: 0.6 to
map.on("load", function() {
// create and add the layer
var mil = new esri.layers.MapImageLayer({
'opacity': 0.6,
});

GSAP pause animation in a function

I have a set of buttons that when clicked show a sort of pop up and some simple animations. Each pop up contain the same animations for most of the content. Each pop up also has its own sets of animations so I have done the following to get this to work correctly.
$gridTitles.click(function() {
const tl = new TimelineMax();
const $pop = $(this).next('.grid__pop');
const $chars = $pop.find(".grid__pop-title span");
const $items = $pop.find(".grid__pop-list li");
const func = $(this).data("graphic-function");
tl.set($pop, {
autoAlpha: 0,
display: 'block',
scale: .5
})
.to($pop, 1, {
autoAlpha: 1,
scale: 1,
ease: Power2.easeInOut
})
.staggerFrom($chars, 0.01, {
autoAlpha: 0,
ease: Power2.easeIn
}, 0.1)
.add(graphicAnimation[func])
.staggerFrom($items, 0.8, {
autoAlpha: 0,
rotationX: 90,
ease: Power2.easeOut
}, .8);
return tl;
});
This runs my pop up code and also using the .add function I call another function that runs the specific animation for the pop up based on a data attribute matching the name of a function in an object.
const graphicAnimation = {
graphicServer: function() {
const tl = new TimelineMax();
const $server = $(".graphic-server__server");
const $one = $(".graphic-server__one");
const $two = $(".graphic-server__two");
const $three = $(".graphic-server__three");
return tl.to($server, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
.to($one, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
.to($two, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
.to($three, 1, {
autoAlpha: 1,
xPercent: "0",
ease: Power2.easeInOut
})
},
// more functions
}
This works great depending on which button is clicked the correct function is called in the object and my animations run. The problem now is that some of these animations are looping and when I close the popup I can't pause them.
Using something like the following I tried to accomplish this
$gridCloses.click(function() {
const tl = new TimelineMax();
const $pop = $(this).parents(".grid__pop");
const func = $(this).parents('.grid__pop').siblings('.grid__title').data("graphic-function");
graphicAnimation[func].pause();
return tl.to($pop, 1, {
autoAlpha: 0,
scale: .5,
display: 'none'
});
});
But calling graphicAnimation[func].pause(); isn't going to work as pause() is a function on the returned timeline from that function. How can I access the current running function and pause / kill it.
So I thought this out and just need to store the timelines in their own object as well
const graphicTimeLines = {
graphicServer : new TimelineMax(),
graphicType: new TimelineMax()
}
so with that in my closing action I can do something like the following to completely reset my loops and have the timeline be available to be called again.
graphicTimeLines[func].pause().progress(0).kill();
graphicTimeLines[func] = new TimelineMax();

OpenLayers 3 - draw polyline vertices only

I'm using OpenLayers 3 and I need to show only the vertices of a polyline. For exemple see this image :
I want to be able to show only the red squares (they can be something else than squares, like circles). Using markers is not an option for performance issue, my lines can be huge (500 000 vertices).
Currently I have a working code :
// Define the style for vertex polyline :
var yellowVertexPolylineStyle = [
new ol.style.Style({
image: new ol.style.Circle({
radius: 1.5,
fill: new ol.style.Fill({
color: 'yellow'
})
}),
geometry: function(feature) {
return new ol.geom.MultiPoint(feature.getGeometry().getCoordinates());
}
})
];
// Create the line :
var lineLayer = new ol.layer.Vector({
zIndex: 1000,
source: new ol.source.Vector({ features: [new ol.Feature({ geometry: myLine })] }),
style: yellowVertexPolylineStyle
});
// Add the layer :
map.addLayer(lineLayer);
But this is causing performance issue when the polyline is quite big (> 10 000 points).
Using an ol.geom.MultiPoint geometry is even worse. Does someone knows a better way?
EDIT : I'm trying this now :
// Define the style for vertex polyline :
var yellowVertexPolylineStyle = [
new ol.style.Style({
image: new ol.style.Circle({
radius: 1.5,
fill: new ol.style.Fill({
color: 'yellow'
})
}),
geometry: function(feature) {
var geom = feature.get('stylegeom');
if (!geom || (geom && geom.getCoordinates().length !== feature.getGeometry().getCoordinates().length) ) {
geom = new ol.geom.MultiPoint(feature.getGeometry().getCoordinates());
feature.set('stylegeom', geom);
}
return geom;
}
})
];
I'll come back here to tell if it works...
You need to cache your style geometry, otherwise it will be calculated for every rendered frame, e.g.
geometry: function(feature) {
var geom = feature.get('stylegeom');
if (!geom) {
geom = new ol.geom.MultiPoint(feature.getGeometry().getCoordinates());
feature.set('stylegeom', geom);
}
return geom;
}
If your feature geometry changes, you'll need to update the style geometry too:
source.on('changefeature', function(evt) {
feature.set('stylegeom', undefined);
});

FullCalendar and Flot Resize Conflict

I've successfully integrated both a Flot line graph and an instance of FullCalendar into my site. They are both on separate pages (although the pages are loaded into a div via AJAX).
I've added the Flot Resize plugin and that works perfectly, re-sizing the line graph as expected. However, it seems to cause an error when resizing the calendar.
Even if I load the calendar page first, when I resize the window I get this error in the console (also, the calendar does not resize correctly):
TypeError: 'undefined' is not an object (evaluating 'r.w=o!==c?o:q.width()')
I was struggling to work out where the error was coming from, so I removed the link to the Flot Resize JS and tried again. Of course the line graph does not resize, but when resizing the calendar, it works correctly.
The div containers for the two elements have different names and the resize function is called from within the function to draw the line graph (as required).
I have tried moving the link to the Flot Resize plugin into different places (i.e. above/below the fullCalendar JS, into the template which holds the graph), but all to no avail.
Does anyone have any idea where the conflict might be and how I might solve it??
Thanks very much!
EDIT: It seems that the error is also triggered when loading the line graph (flot) page AFTER the fullcalendar page even without resizing the window.... Now I am very confused!
EDIT 2: The code which draws the line graph. The function is called on pageload and recieves the data from JSON pulled off the server. When the graph is loaded, I still get the error about shutdown() being undefined.
function plotLineGraph(theData){
var myData = theData['data'];
var myEvents = theData['events'];
var myDates = theData['dates'];
var events = new Array();
for (var i=0; i<myEvents.length; i++) {
events.push(
{
min: myEvents[i][0],
max: myEvents[i][1],
eventType: "Calendar Entry",
title: myEvents[i][2],
description: myEvents[i][3]
}
);
}
function showTooltip(x, y, contents) {
$('<div id="tooltip">' + contents + '</div>').css( {
position: 'absolute',
display: 'none',
top: y + 5,
left: x + 5,
border: '1px solid #fdd',
padding: '2px',
'background-color': 'black',
opacity: 0.80
}).appendTo("body").fadeIn(200);
}
var previousPoint = null;
$("#placeholder").bind("plothover", function (event, pos, item) {
$("#x").text(pos.x.toFixed(2));
$("#y").text(pos.y.toFixed(2));
if ($("#enableTooltip:checked").length == 0) {
if (item) {
if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex;
$("#tooltip").remove();
var x = item.datapoint[0].toFixed(2),
y = item.datapoint[1].toFixed(2);
if(item.series.label != null){
showTooltip(item.pageX, item.pageY,
item.series.label + " of " + y);
}
}
}
else {
$("#tooltip").remove();
previousPoint = null;
}
}
});
var d1 = [
myData[0], myData[1], myData[2], myData[3], myData[4],
myData[5], myData[6], myData[7], myData[8], myData[9],
myData[10], myData[11], myData[12], myData[13], myData[14],
myData[15], myData[16], myData[17], myData[18], myData[19],
myData[20], myData[21], myData[22], myData[23], myData[24],
myData[25], myData[26], myData[27], myData[28], myData[29]
];
var markings = [
{ color: '#FFBDC1', yaxis: { from: 0, to: 2 } },
{ color: '#F2E2C7', yaxis: { from: 2, to: 3.5 } },
{ color: '#B6F2B7', yaxis: { from: 3.5, to: 5 } }
];
$.plot($("#placeholder"), [
{label: "Average Daily Rating", data: d1, color: "black"}
], {
events: {
data: events,
},
series: {
lines: { show: true },
points: { show: true }
},
legend: { show: true, container: '#legend-holder' },
xaxis: {
ticks:[
myDates[0], myDates[1], myDates[2], myDates[3], myDates[4],
myDates[5], myDates[6], myDates[7], myDates[8], myDates[9],
myDates[10], myDates[11], myDates[12], myDates[13], myDates[14],
myDates[15], myDates[16], myDates[17], myDates[18], myDates[19],
myDates[20], myDates[21], myDates[22], myDates[23], myDates[24],
myDates[25], myDates[26], myDates[27], myDates[28], myDates[29]
],
},
yaxis: {
ticks: 5,
min: 0,
max: 5
},
grid: {
backgroundColor: { colors: ["#fff", "#eee"] },
hoverable: true,
clickable: true,
markings: markings
},
selection: {
color: 'white',
mode: 'x'
},
});
$('#placeholder').resize();
$('#placeholder').shutdown();
}
EDIT 3:
The calendar is called like this:
function showCalendar() {
var date = new Date();
var d = date.getDate();
var m = date.getMonth();
var y = date.getFullYear();
$('#fullcalendar').fullCalendar({
header: {
left: 'prev',
center: 'title',
right: 'next'
},
clickable: true,
firstDay: 1,
eventSources: [
{
url: '/populate-calendar/{{theProductUuid}}/',
color: 'black',
data: {
text: 'text'
}
}
],
eventClick: function(calEvent, jsEvent, view) {
var startDate = $.fullCalendar.formatDate(calEvent.start, 'yyyy-MM-dd');
var endDate = $.fullCalendar.formatDate(calEvent.end, 'yyyy-MM-dd');
var eventId = calEvent.uuid;
$('#modal-event-title').text(calEvent.title);
$('#edit-event-name').val(calEvent.title);
$('#edit-start-date').val(startDate);
$('#edit-end-date').val(endDate);
$('#edit-event-text').val(calEvent.text);
$('#edit-event-btn').attr('data-uuid', eventId);
$('#modal-edit-event').on('click', '#delete-btn', function(){
deleteCalendarEvent(eventId);
});
$('#modal-edit-event').modal();
},
});
}
The AJAX to load the page containing the flot chart:
function loadDetailedReports(uuid){
$('#product-content').fadeOut('slow', function(){
$('#product-content').empty();
$('#whole-product-sub-nav .active').removeClass('active');
$('#detailed-reports-content').load('/detailed-reports/' + uuid + '/', function(){
$('#detailed-reports-btn').addClass('active');
$('#detailed-reports-content').fadeIn('slow', function(){
if (authorized){
setLocationHash('loadDetailedReports&' + uuid);
getChartData(uuid);
} else {
setLocationHash('');
}
});
});
});
}
And the AJAX to load the page containing the calendar:
function loadCalendar(uuid){
$('#detailed-reports-content').empty().hide();
$('#product-content').fadeOut('slow', function(){
$('#whole-product-sub-nav .active').removeClass('active');
$('#product-content').load('/calendar/' + uuid + '/', function(){
$('#calendar-btn').addClass('active');
$('#product-content').fadeIn('slow', function(){
if (authorized){
setLocationHash('loadCalendar&' + uuid);
} else {
setLocationHash('');
}
showCalendar();
});
});
});
}
The calls to .resize and .shutdown are there because I was under the impression that they are necessary to achieve the resizing function and in response to your earlier comment regarding shutdown...... They're quite possibly n00b errors........?!?!
It looks like this is triggering on line 198 of jquery-resize:
data.w = w !== undefined ? w : elem.width();
This sounds like a race-condition stemming from the way you load different content into the same div. Flot binds the resize event to the chart div, and only un-binds it if the plot is destroyed cleanly.
EDIT: Looking at your code, my first suggestion would be to get rid of the resize and shutdown calls at the end of plotLineGraph. The resize plugin doesn't require any setup; it hooks into Flot to attach automatically to any new plot. So your call to resize is actually to jQuery's resize event trigger, which may be what's causing the error.
EDIT #2: I'm still not clear on your structure, but to generalize: anywhere that you might be getting rid of #placeholder (via emptying its parent or anything like that) you should first call shutdown on the plot object. If you aren't keeping a reference to it, you can do it like this: $("#placeholder").data("plot").shutdown(); but then have to account for the fact that it's undefined prior to the creation of your first plot.
If that still doesn't work, I'd need to see a live (simplified) example to make any further suggestions.