It has been my experience that with Edge that the "target" received by your IntersectionObserver callback has been set to the newly scrolled-in element rather than (like Chrome and Firefox) where it still reflects the element that started scrolling-out. I played with smaller thresholds but sadly my function thinks the scroll snap fell short and don't bother changing the current image marker dot.
I'm looking at separate issues with Firefox as well :-(
Apart from waiting 'n' nanoseconds after a scroll event is there a better way to know where your carousel is at?
Guess I'll just take the "IF" out and see if I can fix FF.
EDIT: Firefox only seems to allow me to observe 2 elements for my intersection observer. Do I have to new-up a separate IntersectionObserver object for each element being observerved?
carousel = document.getElementById("carousel");
let observerOptions = {
root: carousel,
rootMargin: "0px",
threshold: [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]
};
bannerObserver = new IntersectionObserver(imageScrolled, observerOptions);
for (let i = 0; i < 5; i++) {
bannerObserver.observe(document.getElementById("d" + i));
}
function imageScrolled(divContainers) {
divContainers.some(function (imgContainer, containerIndex) {
let targetDiv = imgContainer.target;
if (imgContainer.intersectionRatio > 0.5) {
if (targetDiv.dataset.imgId != currDot) {
clearTimeout(bannerLoop);
dots[currDot].style.backgroundColor = "";
currDot = targetDiv.dataset.imgId;
dots[currDot].style.backgroundColor = DOT_COLOR;
bannerLoop = setTimeout(scrollBanner, bannerInterval);
return true;
}
}
});
}
The IntersectionObserver is currently too unreliable/inconsistent so I went for a simple onscroll event: -
function onTheMove(e) {
for (let i=0; i < e.srcElement.children.length; i++) {
if (e.srcElement.scrollLeft == e.srcElement.children[i].dataset.imgId * e.srcElement.children[i].clientWidth) {
if (e.srcElement.children[i].dataset.imgId != currDot) {
clearTimeout(bannerLoop);
dots[currDot].style.backgroundColor = "";
currDot = e.srcElement.children[i].dataset.imgId;
dots[currDot].style.backgroundColor = DOT_COLOR;
bannerLoop = setTimeout(scrollBanner, bannerInterval);
break;
}
}
}
}
caniuse.com is wrong on this one :-(
Related
I am working on an application where I'd like to provide overlays of different animations onto a range of videos using p5js. I'm looking to organize my classes of animation types so that each animation has a similar structure to update and destroy objects during each loop. My plan is to have an array of animations that are currently "active" update them each iteration of the loop and then destroy them when they are completed. I built a class to fade text in this manner but I'm getting some weird flashy behavior that seems to occur every time a new animation is triggered in the middle of another animation. I've been trying to debug it but have been unsuccessful. Do you have any suggestions as to:
(1) if this is due to my code structure? (and maybe you have a suggestion of a better way),
or
(2) I'm doing something else incorrectly?
Here is the code:
// create an array of currently executing animations to update
// each animation class needs to have one function and one attribute:
// (1) update() -- function to move the objects where ever they need to be moved
// (2) done -- attribute to determine if they should be spliced out of the array
var animations = [];
//////////////////////////////////////////
// Global Variables for Animations //
//////////////////////////////////////////
let start = false;
let count = 0;
function setup(){
let canv = createCanvas(1920, 1080);
canv.id = "myP5canvas";
background(0);
}
function draw(){
background(0);
// Check things to see if we should be adding any animations to the picture
var drawText = random(100);
if (drawText > 98) {
//if (start == false) {
let r = 255;
let g = 204;
let b = 0;
let x = random(width-10);
let y = random(height-10);
animations.push(new TextFader("Wowwwzers!", 100, 'Georgia', r, g, b, x, y, count));
start = true;
count += 1;
}
// Update animations that exist!
for (var i=0; i < animations.length; i++) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}
}
// EXAMPLE ANIMATION
// TEXT FADE
let TextFader = function(words, size, font, red, green, blue, xloc, yloc, id) {
this.id = id;
console.log("create fader: " + this.id);
// translating inputs to variables
this.text = words;
this.size = size;
this.font = font;
// To Do: separating out each of the values until I figure out how to fade separately from the color constructor
this.red = red;
this.green = green;
this.blue = blue;
this.xloc = xloc;
this.yloc = yloc;
// Maybe add customization in the future for fading...
this.fade = 255;
this.fadeTime = 3; // in seconds
this.fadeIncrement = 5;
// Variables to use for destruction
this.createTime = millis();
this.done = false;
}
TextFader.prototype.update = function() {
// Update the fade
// If the fade is below zero don't update and set to be destroyed
this.fade -= this.fadeIncrement;
if (this.fade <= 0) {
this.done = true;
} else {
this.show();
}
}
TextFader.prototype.show = function() {
textFont(this.font);
textSize(this.size);
fill(this.red, this.green, this.blue, this.fade);
text(this.text, this.xloc, this.yloc);
console.log("Drawing: " + this.id + " fade: " + this.fade + " done: " + this.done);
}
Yay, I've got you an answer! It works like expected when you reverse the for loop that loops over the animations.
Because you splice elements of the same array inside the loop, some elements are skipped. For example; animations[0].done = true and gets removed. That means that animations[1] is now in the spot of animations[0] and animations[2] is now in the spot of animations[1].
The i variable is incremented to 1, so on the next loop, you update animations[1] (and skip the animation that is now in animation[0]).
When you reverse the loop, everything before the element you splice stays the same and nothing is skipped.
For example; animations[2].done = true and gets removed. That means that animations[1] is still in the spot of animations[1].
The i variable is decremented to 1, so on the next loop, you update animations[1] and don't skip any elements.
// Update animations that exist!
for (var i = animations.length - 1; i >= 0; i--) {
// update the position/attributes of the animation
animations[i].update();
// check if the animation is done and should be removed from the array
if (animations[i].done) {
//console.log("SPLICE: " + animations[i].id);
animations.splice(i, 1);
}
}
I have a circle, consisting of 12 arc segments and I want to allow the user to see the transition from the start pattern to the end pattern. (there will be many start and end patterns).
Here is my code so far:
http://codepen.io/blazerix/pen/jrwNAG
function playAnimations(){
var totalLength = document.getElementsByClassName("container")[0].children.length
for(var i = 0; i < totalLength; i++){
var current_pattern = document.getElementsByClassName("container")[0].children[i]
console.log(current_pattern)
for(var j = 0; j < 12; j++){
$('#LED' + (j+1) ).css('transition-duration', '0s');
$('#LED' + (j+1) ).css({fill: current_pattern.children[1].children[j].style.backgroundColor});
}
setTimeout(function () {
for(var k = 0; k < 12; k++){
$('#LED' + (k+1) ).css('transition-duration', "" + current_pattern.children[3].children[0].value + "ms");
$('#LED' + (k+1) ).css({fill: current_pattern.children[2].children[k].style.backgroundColor});
}
}, 150);
}
}
The outer for loop goes through all of the patterns, and the two inner for loops will go through the start and end pattern respectively. For some reason, my program only displays the animation of the very last pattern. I suspect this is because the code is executing really quickly - however I am unsure of how to fix this.
Does anyone know a good workaround or what I could possibly do to rectify this issue? Any feedback or help is appreciated.
Ok, not entirely understanding all the parts of your code, I've whipped this up. It doesn't work just yet, but you may get the idea of what I'm trying to do: wait 250 milliseconds before you fire off the next animation, once you run out of siblings, bounce to the other animation. I can't spend any more time on this, but I hope this gets you where you want to be:
function playAnimations() {
var $patternHolder = $(".container");
playAnimation($('#LED1'), 0, $patternHolder, 1, 1);
}
function playAnimation($target, index, $patternHolder, childPatternIndex, animationNumber) {
//just set both fill color and transition in the object you pass in:
//.eq() chooses a child, returns jQuery object of that child by index
//Use jQuery to get background-color style
$target.css({ fill: $patternHolder.children(childPatternIndex).children().eq(index).css("background-color"), transition: "0s" });
setTimeout(function () {
if ($target.parent().next().length > 0) {
playAnimation($target.parent().next(), index++);
} else if (animationNumber == 1) {
playAnimation($("#LED1"), 0, patternHolder, 3, 2);
}
}, 250);
}
here's a link to something I've been working on
http://79.170.40.170/johnhartmanportfolio.com/
and a couple of notes
I built a simple 3 band graphic eq and am currently hooking up the filters.
if you click the green button a sine wave will play; the red button will stop it.
if you move any fader (any direction)the low pass filter will tick down (if you open console i am outputting the current filter value to the log)
the problem: I am using if loops to compare the values the problem is once I reach 0 it's kind of like my other loop never gets looked at. I don't know what I am doing wrong perhaps an if loop is not appropriate? that being said its made me revisit assignment and operators but I'm still stuck any help would be much appreciated.
(just the js)
$(document).ready(function() {
var context = new AudioContext();
var oscillator = context.createOscillator();
var volume = context.createGain();
var biquadFilter = context.createBiquadFilter();
biquadFilter.type = "lowshelf";
var eqValue = 1000;
biquadFilter.frequency.value = eqValue;
biquadFilter.gain.value = 1;
volume.gain.value = 0;
oscillator.start(0)
$("#playButton").click(function() {
oscillator.connect(volume);
volume.connect(biquadFilter)
biquadFilter.connect(context.destination);
volume.gain.value = 1;
});
$("#stopButton").click(function() {
volume.gain.value = 0;
});
$(".faderStyle").on("slide", function() {
if (eqValue = 0) {
biquadFilter.frequency.value += 10;
console.log(biquadFilter.frequency.value)
}
if (eqValue = 1000) {
biquadFilter.frequency.value -= 10;
console.log(biquadFilter.frequency.value)
return (eqValue);
}
return (eqValue)
});
$(".faderStyle").slider({
orientation: "vertical",
range: "min",
min: 0,
max: 1000,
value: 60
});
});
Your if's should use either == or === for comparison.
The equals operator will always set your variable's value, which is not what you want.
eqValue = 0 will always evaluate as false and should be eqValue === 0
eqValue = 1000 will always evaluate as true and should be eqValue === 1000
I’m having a problem here since a couple days ago with Google Chrome. I love pixel art and I'm trying to create a huge animated map using html5.
Everything was ok until I started to use more than 5 or 6 canvas at same time.
In Firefox and Internet explorer the map has no problems, but in Chrome the CPU reaches 70% and sometimes even more.
Can you give me some good tips about how solve this kind of issues in Chrome?
I've been searching on Internet about improve performance in Chrome but nothing is helping me.
Thanks for your help.
Just for reference this is the map:
http://pixelslivewallpaper.github.io/jadsdsengine/Zelda.htm
Solution:
The easy way to fix this problem is stopping the animation of all the canvas outside the user visible area and resuming them later. The way for calculate this area is capturing the scroll position.
Apparently, Firefox and Internet explorer are doing this job automatically, but in Google Chrome you need do it manually.
Here is the code:
this.loadIsVisibleOptions = function () {
if (this.stopAnimationWhenIsNotVisible) {
window.addEventListener("scroll", function (event) { currentJadsdsEngine.isVisible(document.getElementById(canvasID), currentJadsdsEngine) });
}
}
this.isVisible = function (element, obj) {
if (element.offsetWidth === 0 || element.offsetHeight === 0) {
currentJadsdsEngine.stop();
}
else {
var rects = element.getClientRects();
var result = false;
var tempClientHeight = document.documentElement.clientHeight;
var tempClientWidth = document.documentElement.clientWidth;
for (var i = 0, l = rects.length; i < l; i++) {
if (rects[i].top + rects[i].height > 0 ? rects[i].top <= tempClientHeight : (rects[i].bottom > 0 && rects[i].bottom <= tempClientHeight) == true) {
if (rects[i].left + rects[i].width > 0 ? rects[i].left <= tempClientWidth : (rects[i].right > 0 && rects[i].right <= tempClientWidth) == true) {
result = true;
break;
}
}
}
if (result) {
currentJadsdsEngine.resume();
}
else {
currentJadsdsEngine.stop();
}
}
}
Inspiration:
How do I check if an element is really visible with JavaScript?
I am building a C++ application based on QML.
To make it simple :
In my main QML file, I have a button (Rectangle) calling a JavaScript function (defined in an external JS file) when clicked:
// My JS file linked to the main QML window
[...]
function actionOnButtonClicked()
{
var x = 0;
var y = 0;
for(var i = 0; i < 3; i++)
{
createObject(x, y);
x = x + 10;
y = y + 10;
}
}
As you can see, in this function, I call n (= 3 here) times another JS function to dynamically create several QML objects to add to the scene:
function createObject(xPosition, yPosition)
{
component = Qt.createComponent("Symbol.qml");
component.createObject(windowApp, {"x": xPosition, "y": yPosition});
}
This is working fine. But the created object (Symbol) appears in the windowApp with a translation animation (around 1sec.), and I would like to wait for the first object's animation to complete before creating the second one...
As we cannot use setTimeOut() JavaScript function in QML, I wonder how I could achieve this. I do not see how I could make use of the QML Timer object or even PauseAnimation...
Does somebody know how to add a delay between 2 QML JavaScript operations ?
I think this QML Timer type can help you achieve what you want.
import QtQuick 2.0
Item {
Timer {
interval: 500; running: true; repeat: true
onTriggered: time.text = Date().toString()
}
Text { id: time }
}
You could probably do it so that you only create one "Symbol" from your button action and trigger a new symbol on some event in the new object. Perhaps the animation ending triggers an event that you could use ?
Its been a while, I have missed QML. But let me try to suggest a solution. I guess this might work, if you are calling that translationAnimation.running = true in Component.onComlpeted event. I have posted a stupid answer before. Now I replace it with a lazy/ugly way to do this. This is probably not the right way to do it, though this code may help your use case.
CreateObject.js
.pragma library
var objects = null;
var objectCount = 0;
var i = 0;
var mainWin;
var x = 0;
var y = 0;
function calledOnbuttonAction(parentWindow)
{
if(objects === null)
{
mainWin = parentWindow;
x = 0;
y = 0;
objects = new Array();
createObject(x,y);
}
else
{
if(x <= mainWin.width)
x = x + 28;
else
{
x = 0;
if(y <= mainWin.height)
y = y + 28;
else
{
console.debug("Exceeded window area!")
return;
}
}
createObject(x,y);
}
}
function createObject(xPos, yPos)
{
i++;
var component = Qt.createComponent("Object.qml");
objects[objectCount++] = component.createObject(mainWin, {"x": xPos, "y": yPos});
}
main.qml
import QtQuick 1.1
import "CreateObjects.js" as CreateObject
Rectangle {
id: mainWindow
width: 360
height: 360
Text {
text: qsTr("Click inside window")
anchors.centerIn: parent
font.pixelSize: 18
}
MouseArea {
anchors.fill: parent
onClicked: {
CreateObject.calledOnbuttonAction(mainWindow); //passing the parent window object
}
}
}
Object.qml //Symbol in your case
//The Symbol
import QtQuick 1.1
import "CreateObjects.js" as CreateObject
Rectangle {
id: obj
width: 25
height: 25
gradient: Gradient {
GradientStop {
position: 0
color: "#d11b1b"
}
GradientStop {
position: 1
color: "#ea4848"
}
}
property alias animationStatus: completedAnimation
NumberAnimation {
id: completedAnimation;
target: obj;
property: "opacity";
duration: 800;
from: 0;
to: 1.0;
onRunningChanged: {
if(!running && CreateObject.i < 900) // Decrease or increase the value according to the number of objects you want to create
{
CreateObject.calledOnbuttonAction();
}
}
}
Component.onCompleted: completedAnimation.running = true;
}