Firefox/Gecko unable to animate transform="rotate(…)" in SVG? - javascript

In the page I'm working on, when the user clicks on an object, one SVG group rotates out of the way while another rotates in.
The code as it is works just fine in WebKit, but it isn't working at all in Gecko. Here is the block of code that is not being executed by Gecko:
var totStep = dur*2/msrate, step=0;
window.timer = window.setInterval(function(){
if(step<totStep/2){
var inangle = -50*easeIn(step,totStep/2);
iris.setAttribute("transform","rotate("+inangle+" 23 -82)");}else{
var prog = easeOut2((step-(totStep/2)),totStep/2);
var outangle = 50*prog;
var down = 400*prog;
vinyl.setAttribute("transform","rotate("+(-50+outangle)+" 986 882)");
needle1.setAttribute("transform","translate(0 "+(-400+down)+")");
buttons.setAttribute("transform","translate(0 "+(-400+down)+")");}
step++;
if (step > totStep) {window.clearInterval(window.timer); return}
});
Most of this code is adapted from a function which opens the eye when the page is loaded, and that function works fine in Gecko, which is why this is enigmatic to me.
You can see the page with all of its source code at this page. The problematic function is written in the linked eye.js. The problem occurs when the user clicks on "DJ Docroot" under the "Music" section of the menu, which is accessed by clicking anywhere.

You're missing a second argument to setInterval to specify the interval at which the function should be called. So, for example, the following code works:
window.timer = window.setInterval(function(){
if(step<totStep/2){
var inangle = -50*easeIn(step,totStep/2);
iris.setAttribute("transform","rotate("+inangle+" 23 -82)");}else{
var prog = easeOut2((step-(totStep/2)),totStep/2);
var outangle = 50*prog;
var down = 400*prog;
vinyl.setAttribute("transform","rotate("+(-50+outangle)+" 986 882)");
needle1.setAttribute("transform","translate(0 "+(-400+down)+")");
buttons.setAttribute("transform","translate(0 "+(-400+down)+")");}
step++;
if (step > totStep) {window.clearInterval(window.timer); return}
},10);
Webkit probably just assumes a default value.
Also, just a suggestion, in the future, it might be easier to spot errors like these if you adopt code conventions that will make your code more legible: http://javascript.crockford.com/code.html
A tool like jslint will help with this.

Related

Adobe flash professional CC gotoAndPlay bug

First of all, hello.
I have to develop a simple game in flash, usign HTML5 Canvas and JavaScript. The game, however poor, is pretty much implemented. My problem is, that after you play the game, it shows a try again button, designed to go back to frame 1. When i click the button it goes to frame 1 and returns to frame 25. Any ideas?
The last frame has this code:
var root = this;
this.stop();
this.tryAgain_btn.addEventListener("click", mouseClickHandler);
function mouseClickHandler(e) {
root.gotoAndPlay(0);
};
And frame one has this code:
var self = this;
this.stop();
this.play_btn.addEventListener("click", go);
function go()
{
self.gotoAndPlay(1);
}
Any ideas on how to solve this? Thank you.
Are you developing this in html5 or in actionscript? Based on your picture of your timeline it looks like you're using actionscript, not a HTML5 Canvas with javascript.
Besides that, try using gotoAndStop(1); rather than gotoAndPlay(0) and removing your stop(); methods. Let me know in the comments if it works but since your question is slightly unclear I don't have a grasp on what you're doing.
EDIT
Example, in case you don't understand:
var root = this;
this.stop();
this.tryAgain_btn.addEventListener("click", mouseClickHandler);
function mouseClickHandler(e) {
root.gotoAndStop(1);
}
EDIT 2
Take a shot at using
this.tryAgain_btn.addEventListener("click", function (event)
{
this.gotoAndPlay(1);
});

Resolving latency with setTimeout for Drum Machine

I am currently working on a drum machine and I am using setTimeout to make it run. Here is the heart of the code:
var sequencerRun = function(){
var currentTime = 0
var starting = 200;
for(var k = 0; k < 16; k++){
$(".instrument td .beat" + k).each(function(){
setTimeout(blinker, currentTime,$(this));
})
currentTime += starting;
}
}
var timerId, setInt;
var runSeq = function(){
setInt = setInterval(sequencerRun,3200);
}
$('.play').click(function(){
stopped = false
sequencerRun();
runSeq();
})
$('.stop').click(function(){
clearInterval(setInt);
stopped = true;
})
The drum machine has a matrix HTML structure built using a table. When .play is clicked a scheduling process occurs, which is encapsulated in sequencerRun. This involves a run through the columns of my matrix to determine whether there should be a drum hit or not. This is done through blinker. The scheduling creates a check on each column 1 - 16 at times 0,200,...,3200 respectively. This is what creates the effect of a sequencer. I also have a setInterval that reruns this process every 3200, which is how it takes for a run to finish.
Programmatically my code seems to make sense and my hope was that it would execute on time. The thing is that my actual app tends to stutter a lot and is stuttering even more since I deployed it. Here is a deployed version of my app.
This stuttering side effect can be best heard when you click on a full row. My question here is can anyone tell if this side effect is a result of setTimeout's timing inconsistency and if so how could I go about fixing this? Or is this related to something else that I am missing?
I think the stuttering issue has more to do with you not preloading the instruments but rather loading them on every hit, more than it has to do with settimeout.
In any case, I think I would have solved this differently. Rather than setting a fresh timeout for each beat, create one beat timeout and put the logic in there. Something like (pseudo-code-ish, lots of stuff missing just the general idea):
var MAX_BEATS = 16; // I think you had 16 beats in your example?
var BPM = 200;
var preloadedInstruments = [];
function preloadInstruments(){
for(i of myInstruments) { // myInstruments would be a list of your instruments (probably just strings with a filename)
preloadedInstruments.push(new instrument(i)); // You need an instrument class, that would preload the sound and provide a play function
}
}
var currentbeat = 1;
function beat() {
var activeInstruments = getActiveInstruments(currentbeat); // You could cache this also for every beat, but I think a simple loop here would be quick enough
for(instrument of activeInstruments) {
preloadedInstruments[instrument].play(); // play method of the instrument class called
}
currentbeat++;
if (currentbeat > MAX_BEATS) {
currentbeat = 1;
}
}
setInterval(beat, 60e3 / BPM);

Raphael js drag inside javascript object

I had a little problem today and thought I could try stack overflow. I'll be short and sweet (I removed lot of code to make this clear).
I recently discovered raphaeljs and I like it.
I make some circle draggable and it works fine like this:
Working script:
<script>
var paper = Raphael(100,100,500,500);
var circ = paper.circle(50,50,10)
var start = function(){ };
var move = function(dx,dy)
{
this.attr({cx: this.ox + dx, cy: this.oy + dy});
};
var up = function(){};
circ.drag(move,start,up);
<script>
Ok fine, it works and all functions are called properly!
BUT!
I want my move ,start ,up functions to be inside an object and not in the main page
SO
here's my next code
<script src="myobject.js"></script>
<script>
var paper = Raphael(100,100,500,500);
var myobj = new myobject("12","12","6");
<script>
Content of myobject.js :
function myobject(vx,vy,vr)
{
var x,y,r;
x=vx;y=vy;r=vr
paper.circle(x,y,r);
var start = function(){};
var move = function(dx,dy){};
var up = function(){};
this.drag(move,start,up); // error here this line crash
}
I cannot find how to use the drag function inside my object to make it draggable.
Well, that's it. I hope I've been clear and pardon me if there's anything wrong with the way I made this post but it is my first one!
Thanks to everyone that will try to help me!
Wilson
Inside myobject, this variable points to this object, and not to Raphael circle. As myobject does not have drag function, the code yields an error. To make it work, you must refer to Raphael object that has to be dragged, i.e.
function myobject(vx,vy,vr) {
...
this.circle = paper.circle(x,y,r);
...
this.circle.drag(move,start,up);
...
}
Fine, #Hubert OG was right but I also had to change the way I declared my function from
var start()=function
to
this.start() = function
WARNING: Fill your circle/forms because if you don't you have to click the fine border to move it around.

innerHTML can't be trusted: Does not always execute synchronously

To see the problem in action, see this jsbin. Clicking on the button triggers the buttonHandler(), which looks like this:
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking";
longPrimeCalc();
}
You would expect that this code changes the text of the div to "thinking", and then runs longPrimeCalc(), an arithmetic function that takes a few seconds to complete. However, this is not what happens. Instead, "longPrimeCalc" completes first, and then the text is updated to "thinking" after it's done running, as if the order of the two lines of code were reversed.
It appears that the browser does not run "innerHTML" code synchronously, but instead creates a new thread for it that executes at its own leisure.
My questions:
What is happening under the hood that is leading to this behavior?
How can I get the browser to behave the way I would expect, that is, force it to update the "innerHTML" before it executes "longPrimeCalc()"?
I tested this in the latest version of chrome.
Your surmise is incorrect. The .innerHTML update does complete synchronously (and the browser most definitely does not create a new thread). The browser simply does not bother to update the window until your code is finished. If you were to interrogate the DOM in some way that required the view to be updated, then the browser would have no choice.
For example, right after you set the innerHTML, add this line:
var sz = elm.clientHeight; // whoops that's not it; hold on ...
edit — I might figure out a way to trick the browser, or it might be impossible; it's certainly true that launching your long computation in a separate event loop will make it work:
setTimeout(longPrimeCalc, 10); // not 0, at least not with Firefox!
A good lesson here is that browsers try hard not to do pointless re-flows of the page layout. If your code had gone off on a prime number vacation and then come back and updated the innerHTML again, the browser would have saved some pointless work. Even if it's not painting an updated layout, browsers still have to figure out what's happened to the DOM in order to provide consistent answers when things like element sizes and positions are interrogated.
I think the way it works is that the currently running code completes first, then all the page updates are done. In this case, calling longPrimeCalc causes more code to be executed, and only when it is done does the page update change.
To fix this you have to have the currently running code terminate, then start the calculation in another context. You can do that with setTimeout. I'm not sure if there's any other way besides that.
Here is a jsfiddle showing the behavior. You don't have to pass a callback to longPrimeCalc, you just have to create another function which does what you want with the return value. Essentially you want to defer the calculation to another "thread" of execution. Writing the code this way makes it obvious what you're doing (Updated again to make it potentially nicer):
function defer(f, callback) {
var proc = function() {
result = f();
if (callback) {
callback(result);
}
}
setTimeout(proc, 50);
}
function buttonHandler() {
var elm = document.getElementById("progress");
elm.innerHTML = "thinking...";
defer(longPrimeCalc, function (isPrime) {
if (isPrime) {
elm.innerHTML = "It was a prime!";
}
else {
elm.innerHTML = "It was not a prime =(";
}
});
}

Avoiding lag caused by hoisting

I want to log 'tapped' and execute the HUD asap, but oauth_upload_photo is causing it to lag (apparently because of hoisting). How can I snap the HUD instantly??
var submit_post = function submit_post(){
console.log('tapped');
// Show HUD
plugins.navigationBar.hideRightButton();
var hud = document.getElementById("hud");
hud.style.display = 'block';
// Get the image
var image = document.getElementById('myImage');
var imageURI = image.src;
// Get the caption from the textarea
var cap = document.getElementById('tar');
var caption = cap.value;
// Call upload photo
oauth_upload_photo(imageURI,caption);
};
Your issue (which needs a lot more explanation before we could understand what you're actually asking about) has nothing to do with javascript variable hoisting. All hoisting does is cause variables to be defined at the top of the function,regardless of where their initial declaration is located in the function. It doesn't change the execution order of any statements.
Also, in some browsers console.log() is not guaranteed to be completely synchronous and the display of the data in the log window is not necessarily immediate either. There is sometimes a delay before it actually logs. I don't know if this is caused by marshalling data across process boundaries, general repaint logic or some other internal implementation issue.
You may also want to change this:
var submit_post = function submit_post(){
to this:
var submit_post = function (){
or even this:
function submit_post() {
so you aren't double defining the same symbol.

Categories

Resources