Scroll with animation a WKWebView using pure Javascript - javascript

I use the following code which should scroll a webview at the given offset with given animation duration. But it doesn't work. I'm not very experienced with Javascript
- (NSString *)jsCodeScrollTo:(NSInteger)offset withAnimationDuration:(NSInteger)animationDuration
{
return [NSString stringWithFormat:#" scrollTo(document.documentElement, %ld, %ld); function scrollTo(element, to, duration) { if (duration < 0) return; var difference = to - element.scrollTop; var perTick = difference / duration * 10; setTimeout(function() { element.scrollTop = element.scrollTop + perTick; if (element.scrollTop === to) return; scrollTo(element, to, duration - 10); }, 10); }", offset, animationDuration];
}
It outputs:
scrollTo(document.documentElement, 2841, 2000);
function scrollTo(element, to, duration) { if (duration < 0) return;
var difference = to - element.scrollTop;
var perTick = difference / duration * 10;
setTimeout(function() { element.scrollTop = element.scrollTop + perTick;
if (element.scrollTop === to) return;
scrollTo(element, to, duration - 10); }, 10); }
I've used code posted in this thread: Cross browser JavaScript (not jQuery...) scroll to top animation
Is here a problem with JS code? or it has issues when running in WebKit (compatibility issues) ?

The solution is, first to insert JS function definition then to call it.
- (void)scrollTo:(NSInteger)offset withAnimationDuration:(NSInteger)animationDuration {
[self insertJavascriptAnimationFunction];
[webView evaluateJavaScript:[NSString stringWithFormat:#"scrollTo(document.body, %ld, %ld);", offset, animationDuration]
completionHandler:nil];
}
- (void)insertJavascriptAnimationFunction
{
[webView evaluateJavaScript:#"function scrollTo(element, to, duration) { var start = element.scrollTop, change = to - start, currentTime = 0, increment = 20; var animateScroll = function(){ currentTime += increment; var val = Math.easeInOutQuad(currentTime, start, change, duration); element.scrollTop = val; if(currentTime < duration) { setTimeout(animateScroll, increment); } }; animateScroll(); } Math.easeInOutQuad = function (t, b, c, d) { t /= d/2; if (t < 1) return c/2*t*t + b; t--; return -c/2 * (t*(t-2) - 1) + b; };"
completionHandler:nil];
}

Related

Defining a static method on a specific type in Javascript

I have a method that calculate a percentage increase between two values.
And in my "class" it looks like this:
percIncrease(a, b) {
let percent;
if (b !== 0) {
if (a !== 0) {
percent = ((b - a) / a) * 100;
} else {
percent = b * 100;
}
} else {
percent = -a * 100;
}
return percent.toFixed(3);
}
now cause I want it reusable from everywhere, I am trying to define it on a type (Number), so I can use it everywhere.
I tried this:
Number.prototype.percIncrease = function (a, b) {
let percent;
if (b !== 0) {
if (a !== 0) {
percent = ((b - a) / a) * 100;
} else {
percent = b * 100;
}
} else {
percent = -a * 100;
}
return percent.toFixed(3);
};
but if I try to use it like this :
Number.percIncrease(a,b);
TypeError: Number.percIncrease is not a function
I am not that good in using Javascript terms, but what I am actually trying, is to declare a static method on a specific type (say, Number).
How I would do this?
EDIT:
Or it doesn't have to be static. Working on a value would do the job to. For example this (pseudo code):
myNumber.percIncrease(inCompareToValue: anotherNumber)
You need a fifferent call of the prototype and maybe change the style a bit.
Number.prototype.percIncrease = function(base) {
let percent;
if (base) {
percent = this
? (base - this) * 100 / this
: base * 100;
} else {
percent = -this * 100;
}
return percent.toFixed(3);
};
console.log(5..percIncrease(4));
When you define a method in the prototype, it's used when you call the method on an instance. Static methods should be added to the type directly, not the prototype:
Number.percIncrease = function (a, b) {
let percent;
if (b !== 0) {
if (a !== 0) {
percent = ((b - a) / a) * 100;
} else {
percent = b * 100;
}
} else {
percent = -a * 100;
}
return percent.toFixed(3);
};
console.log(Number.percIncrease(10, 15));
call your function like that:
Number.prototype.percIncrease(1, 10); //"1,10" are examples

Change value by two functions

I have functions which change value variable totalTime.
When I use first func value equals 189 - 50 (139).
After using second func I want to receive current value - 8 (131) but I have 189 - 8.
What am I doing wrong?
let totalTime = 189; // *
function calcTime(){
let hours = Math.trunc(totalTime/60);
let minutes = totalTime % 60;
let formatted = hours + 'h ' + minutes + 'm';
fullTimeDefault.innerHTML = formatted;
}
function workTimeDecrease() {
if(valueElem[0]) {
if (+valueElem[0].innerHTML === 15) {
return false
} else{
valueElem[0].innerHTML = parseInt(valueElem[0].innerHTML, 10) - step;
}
for(i = 0; i < barElem.length; i++) {
barElem[i].style.width = parseInt(valueElem[0].innerHTML, 10) + '%';
}
totalTime -= 50 // (1!)
}
}
function shortBreakDecrease() {
if(valueElem[2]) {
if (+valueElem[2].innerHTML === 3) {
return false
} else {
valueElem[2].innerHTML = parseInt(valueElem[2].innerHTML, 10) - step;
}
for(i = 0; i < barElem2.length; i++) {
barElem2[i].style.width = parseInt(valueElem[2].innerHTML, 10) + '%';
}
totalTime -= 8 // (2!)
}
}
elem.onclick = function(event) {
if (event.target.closest('.down-time')) {
workTimeDecrease();
calcTime();
} else if (event.target.closest('.up-time')) {
workTimeIncrease();
calcTime();
}
if (event.target.closest('.down-short-break')) {
shortBreakDecrease();
calcTime();
} else if (event.target.closest('.up-short-break')) {
shortBreakIncrease();
calcTime();
}
}
My thoughts are that this line isn't assigning totalTime to your output, though it is being modified internally (also "step" is defined nowhere)
valueElem[0].innerHTML = parseInt(valueElem[0].innerHTML, 10) - step;
Maybe if you pass in step as 50 then 8 in the 2 calls (change headers to take as parameter):
function workTimeDecrease(int step) {
function shortBreakDecrease(int step) { ...
Change:
valueElem[0].innerHTML = parseInt(valueElem[0].innerHTML, 10) - step;
...
valueElem[2].innerHTML = parseInt(valueElem[0].innerHTML, 10) - step;
To:
valueElem[0].innerHTML = totalTime - step;
...
valueElem[2].innerHTML = totalTime - step;
And change both totalTime assignemts to:
totalTime -= step;
You could also do an alert or log to console right after totalTime assignment to make sure that's not the problem (or all over, helps to check values - I use those extensively for debugging, sometimes every single line).

audio files will not work outside of javascript switch statement

I am building an app that allow the user to set a duration to work (workSecs) and when completed a sound alerts the user (buzzer.mp3) and then a break duration (breakSecs) also set by the user begins, and when the break is completed a sound alerts the user (timer.mp3). I am using a switch statement to test when workSecs === 0 and when breakSecs === 0 and the unique alerts go off when either condition is true.
I have a setInterval function for my clock, strangely when I place my switch statement in the setInterval the sound works but it's repetitive because it's in the setInterval function, but when I remove it from within the setInterval function the alert does not work when the condition is true in the switch statement.
I am not sure if it's a scope issue because there are no errors in chrome's developer or firebug.
//audiotype object declare and sound method property
var Audiotypes = {
"mp3": "audio/mpeg",
"mp4": "audio/mp4",
"ogg": "audio/ogg",
"wav": "audio/wav",
soundbits: function (sound) {
var audio_element = document.createElement('audio')
if (audio_element.canPlayType) {
for (var i = 0; i < arguments.length; i++) {
var source_element = document.createElement('source')
source_element.setAttribute('src', arguments[i])
if (arguments[i].match(/\.(\w+)$/i)) source_element.setAttribute('type', Audiotypes[RegExp.$1])
audio_element.appendChild(source_element)
}
audio_element.load()
audio_element.playclip = function () {
audio_element.pause()
audio_element.currentTime = 0
audio_element.play()
}
return audio_element
}
}
}
// Clock object declared
var Clock = {
workSeconds: 0,
breakSeconds: 0,
// startTimer function begins here
startTimer: function () {
var self = this,
workSecs = this.workSeconds + 1,
breakSecs = this.breakSeconds + 1;
//workSecs and breakSecs switch statement begins here
switch (true) {
case (workSecs === 0):
alert('workSecs now 0')
var buzz = Audiotypes.soundbits('sounds/buzzer.mp3');
buzz.play();
break;
case (breakSecs === 0):
alert('breakSecs now 0')
var timer = Audiotypes.soundbits('sounds/timer.mp3');
timer.play();
}
// startTimer interval function begins here
this.interval = setInterval(function () {
if (workSecs > 0) {
workSecs--;
mins.html(Math.floor(workSecs / 60 % 60));
secs.html(parseInt(workSecs % 60));
} else if (breakSecs > 0) {
breakSecs--;
mins.html(Math.floor(breakSecs / 60 % 60));
secs.html(parseInt(breakSecs % 60));
} else if (breakSecs === 0) {
workSecs = self.workSeconds + 1;
breakSecs = self.breakSeconds + 1;
}
self.workSeconds = workSecs;
if (mins.html().length === 1) {
mins.html('0' + mins.html());
}
if (secs.html().length === 1) {
secs.html('0' + secs.html());
}
}, 1000)
}, //there is more code after this point but it's irrelevant to the problem
From your code, I assume you want to count down workSecs again after breakSecs ends and self.workSeconds = workSecs; is unintentional, since next time workSecs basically starts from 0.
The problem with your code is, that the switch statement only gets executed once when you call startTimer, but at that time your workSecs and breakSecs are this.workSeconds + 1 and this.breakSeconds + 1 which will never be zero (except if you set this.workSeconds and this.breakSeconds to -1).
When you put your switch statement into the interval handler, you didn't use proper conditions, and that's why it played it repetitively.
When it was counting breakSecs down to 0, workSecs was always zero and your sound played every second.
I changed your startTimer function to properly count down and use the proper conditions to play the sounds.
var Clock = {
workSeconds: 6,
breakSeconds: 3,
startTimer: function () {
var self = this,
workSecs = this.workSeconds,
breakSecs = this.breakSeconds;
this.interval = setInterval(function () {
if (breakSecs == self.breakSeconds && workSecs === 0) {
var buzz = Audiotypes.soundbits('sounds/buzzer.mp3');
buzz.play();
} else if (breakSecs == 0 && workSecs === 0) {
var timer = Audiotypes.soundbits('sounds/timer.mp3');
timer.play();
workSecs = self.workSeconds,
breakSecs = self.breakSeconds;
}
if (workSecs > 0) {
var m = Math.floor(workSecs / 60 % 60);
var s = workSecs % 60;
mins.text(m < 10 ? '0'+m : m);
secs.text(s < 10 ? '0'+s : s);
workSecs--;
} else if (breakSecs > 0) {
var m = Math.floor(breakSecs / 60 % 60);
var s = breakSecs % 60;
mins.text(m < 10 ? '0'+m : m);
secs.text(s < 10 ? '0'+s : s);
breakSecs--;
}
}, 1000);
},
};
I hope that's what you wanted.

setInterval function not stopping

Level1();
var lvl1count = 0;
function Level1() {
var x = setInterval(function () {
lvl1count++;
var x = 84 + (Math.random() * (cw - 27));
var y = 84 + (Math.random() * (ch - 27));
Enemies.add(new Enemy(x, y, 1, 90));
}, 1000);
if (lvl1count == 4) {
console.log("finish");
clearInterval(x);return;
}
}
i am trying to add an object to a collection every 1000 ms when there is 4 object added i want to clear the Interval and stop the function but i am having the problem and the setInterval function is not stopping
Because the if check is not inside of the code that is being updated. if (lvl1count == 4) is only checked when Level1 is called. Move the check inside of your interval and it will work. You also need to change your variable names so they do not conflict.
function Level1() {
var timer = setInterval(function () {
lvl1count++;
var x = 84 + (Math.random() * (cw - 27));
var y = 84 + (Math.random() * (ch - 27));
Enemies.add(new Enemy(x, y, 1, 90));
if (lvl1count == 4) {
console.log("finish");
clearInterval(timer);
return;
}
}, 1000);
}
var lvl1count = 0;
Level1();
in your case u can use the setTimeout instead the setInterval like this
Level1();
var lvl1count = 0;
function Level1() {
setTimeout(function () {
lvl1count++;
//stuff
if(lvl1count++ <= 5) {
Level1();
}
}, 1000);
}
your latest if statement is executed just once, when you call the Level1 function. So you need to check the counter (the lvl1count variable) everytime you call the function inside setInterval:
var x = setInterval(function () {
lvl1count++;
var x = 84 + (Math.random() * (cw - 27));
var y = 84 + (Math.random() * (ch - 27));
Enemies.add(new Enemy(x, y, 1, 90));
if (lvl1count == 4) {
console.log("finish");
clearInterval(x);
return;
}
}, 1000);
Note: as pointed out by #epascarello this would require a change for a variable name. Since you already use x inside the anonimous function, change the setInterval assignment using another variable e.g.
var anotherVariable = setInterval(function () {
and
clearInterval(anotherVariable);

Page switching algorithm from Nano editor

I'm writing Nano like editor in javascript and have problem with calculating cursor position and file offset for display:
I have two variables _rows and settings.verticalMoveOffset (which is default set to 9 like in Nano) when you move cursor in line 20 and there are 19 rows the cursor is offset by verticalMoveOffset.
I work on this few hours and can't figure out how to map file pointer (line) to editor cursor and offset of the file (the line from which it start the view).
Last try was
if (y >= this._rows) {
var page = Math.floor(y / (this._rows-this._settings.verticalMoveOffset))-1;
var new_offset = (this._rows - this._settings.verticalMoveOffset) * page;
if (this._offset !== new_offset) {
this._view(new_offset);
}
cursor_y = y - (this._rows*page) + this._settings.verticalMoveOffset;
} else {
if (y <= this._rows - this._settings.verticalMoveOffset - 1 &&
this._offset !== 0) {
this._pointer.x = x;
this._pointer.y = y;
this._view(0);
cursor_y = y;
} else if (this._offset !== 0) {
cursor_y = (y - this._settings.verticalMoveOffset) + 1;
} else {
cursor_y = y;
}
}
It work when I swich from page 1st to page 2nd and back, to 3rd page it switch for 1 line to fast, the cursor_y is -1. I try to put plus or minus 1 in various places when calculating page but it didn't work.
Anybody can help me with this?
I found solution (this._offset is set by this._view)
var offset = this._offset + this._rows - cursor_offset;
if (y-this._offset >= this._rows) {
cursor_y = y - offset;
if (this._offset !== offset) {
this._pointer.x = x;
this._pointer.y = y;
this._view(offset);
}
} else if (y-this._offset < 0) {
var new_offset = this._offset - this._rows + cursor_offset;
this._pointer.x = x;
this._pointer.y = y;
this._view(new_offset);
cursor_y = y - new_offset;
} else {
cursor_y = y - offset + cursor_offset + 1;
}

Categories