Need help modifying skew.js for Y axis rotation - javascript

Here's the site in question: https://marks-groovy-project-2fa056.webflow.io/
I want to rotate each letter by 90 degrees on their Y axis while scroll is active, and return them back to their original position as soon as scrolling stops.
Visualization: in top view, a letter should rotate 90deg counter-clockwise when scroll is activated, which will render the letter invisible in front view ( the viewport) while scrolling, and then turn back 90 degrees clockwise when scrolling ends, so that each letter is visible again.
Method: used skew.js and slightly modified it:
skew.js is applied to an entire section. I want to apply it to every instance of a span with id="letter-animation". I've appropriately renamed the constant and referenced the #.
const speed is a remnant from skew.js. I haven't yet figured out how to rewrite it. It expresses the amount of skew as a function of the difference in newPixel/oldPixel. Which I don't want. My rotation needs to be 90deg every time. Once in, once out.
letter.style.transform = "rotateY(45deg)" used to be "rotateY(" + speed + "deg)" in the old script. (technically it was "skewY", not "rotateY" but you get my point). const speed would then be replaced with whatever new constant is appropriate as mentioned in point 2.
I've set up this codepen to isolate the script in question. https://codepen.io/mhedinger/pen/yLJaLmp
const letter = document.querySelector("#letter-animation")
let currentPixel = window.pageYOffset
const looper = function(){
const newPixel = window.pageYOffset
const diff = newPixel - currentPixel
const speed = diff * 11
letter.style.transform = "rotateY(45deg)"
currentPixel = newPixel
requestAnimationFrame(looper)
}
looper()
Here's the tutorial that explains how skew.js works: https://www.superhi.com/video/skew-on-scroll-effect
And here's an example of it working, according to the video tutorial above: https://codepen.io/emgiust/pen/rdOJwQ
const section = document.querySelector("section");
let currentPixel = window.pageYOffset
//looper keeps running and keeps track of where the new pixel is
const looper = function () {
const newPixel = window.pageYOffset;
const diff = newPixel - currentPixel
const speed = diff * 0.35;
section.style.transform = "skewY(" + speed + "deg)"
currentPixel = newPixel;
requestAnimationFrame(looper)
}
looper();
Can anybody help me get this working? So far, the main issue seems to be that they're spans and not sections, but it could also just be my complete absence of understanding of javascript.
I'm hoping to control the transition speed, curve, and delay via css, but if it has to be controlled in JS, i'd also appreciate some advice on how to do this.
Thank you everyone in advance for trying to help.
Cheers,
Mark
NOTE: there is another script running (fullpage.js) which simulates a swiping experience during scroll. deactivate it temporarily to get a better view of what’s happening during scroll while you’re checking things out/setting things up. End-product, a letter animation that synchronously rotates each letter by 90 degrees (rendering the words invisible) for the duration of the swipe/scroll, and returning them to normal once the section snaps into place.

In case anybody is still interested in this, I have figured it out in the meantime.
const letter = document.getElementsByTagName("SPAN");
var timer = null;
window.addEventListener('scroll', function() {
if(timer !== null) {clearTimeout(timer)}
timer = setTimeout(function() {
var i;
for (i = 0; i < letter.length; i++) {
letter[i].style.transform = "rotateY(0deg)";
};
}, 150);
var i;
for (i = 0; i < letter.length; i++) {
letter[i].style.transform = "rotateY(90deg)";
};
}, false);
https://codepen.io/mhedinger/pen/yLJaLmp
EDIT:
I have improved this script for best-practice purposes. This makes it more stable and versatile because it can now be used together with plugins like fullpage.js. Additionally, if desired, the transforms can now also be called manually by referencing the appropriate function.
let letter = document.getElementsByTagName("SPAN");
var timer = null;
var rotateLetter = function() {
Array.from(letter).forEach(function(letter) {
letter.style.transform = "rotateY(90deg)";
});
};
var resetLetter = function() {
Array.from(letter).forEach(function(letter) {
letter.style.transform = "rotateY(0deg)";
});
};
window.addEventListener('scroll', function() {
if(timer !== null) {clearTimeout(timer)}
timer = setTimeout(function() {resetLetter()}, 125);
rotateLetter()
}, false);
https://codepen.io/mhedinger/pen/dypmKZd

Related

simple animation with javascript interval

I'm set up a simple animation with set Interval.
But I wanted to do an animation where it moves to a target position over time.
I used to this back in the flash days and have forgotten the process.
It remember using something like
property = (target - property)/speed;
But having a problem setting that up with the below setup.
I understand there is a 100 ways to do this and even using css but I just want to know how I could implement with that I have now. I just want that ease over time to happen with setInterval.
var sq = document.querySelector('.square');
button = document.querySelector('button');
var interval, toggle = 0, pos=0;
var targetX = 100;
var startX = 0;
button.addEventListener("click", (event) =>{
toggle += 1;
toggle = toggle % 2;
if( toggle > 0){
interval = setInterval(animate, 5);
}else{
clearInterval(interval);
}
});
function animate(){
//pos++
//sq.style.left = pos + 'px';
sq.style.left = (targetX - sq.style.left) / speed
}
Using setInterval() isn't the best choice for animations since it cannot sync to monitor updates. A better choice, and highly recommended, is requestAnimationFrame().
Right now the code uses an interval of 5ms which is way too low. The shortest frame update happens at 16.7ms (at 60Hz screens) so there is much overhead here.
I would also suggest using linear interpolation (AKA lerp) to do the animation, and time duration to define speed. Using interpolation allows you to further feed the t into an easing function.
Example
t is calculated based on start-time, current time and duration
t, now normalized to [0, 1] is fed to lerp function to obtain new position
translateX is used to move div to allow subpixeled animation. Final position can still be set by removing translation and a fixed position for left.
var reqId, // request ID so we can cancel anim.
startTime = 0,
source = 0,
target = 300,
duration = 1000, // duration in ms
sq = document.querySelector("div");
document.querySelector("button").onclick = function() {
startTime = 0; // reset time for t
cancelAnimationFrame(reqId); // abort existing animation if any
reqId = requestAnimationFrame(loop); // start new with time argument
function loop(time) {
if (!startTime) startTime = time; // set start time if not set already
t = (time - startTime) / duration; // calc t
var x = lerp(source, target, t); // lerp between source and target position
sq.style.transform = "translateX("+x+"px)"; // use transform to allow sub-pixeling
if (t < 1) requestAnimationFrame(loop); // loop until t=1
// else { here you can remove transform and set final position ie. using left}
}
// basic linear interpolation
function lerp(v1, v2, t) {return v1 + (v2-v1) * t}
};
div {position:absolute; left:0; top:40px; width:100px;height:100px;background:#c33}
<button>Animate</button>
<div></div>

Move canvas object on touchmove in Javascript

I’m fairly new to web development and I’ve only ever used jQuery to write my scripts. Today however, I’d like to improve my skills and build a little game that could be used on a smartphone as a web app in vanilla JS.
The game’s pretty straightforward:
You hold your phone in portrait mode and control a character that stays at the bottom of the screen and has to dodge objects that are falling on him. The character can only move left or right and thus always stays on the same x-axis. In order to control him, your finger has to stay on the screen. Once you take it off, you lose. Also, the move isn’t triggered by tapping the screen, but by moving your finger left or right.
For now, I’ve only been experimenting to get the hang of touchevents and was able to make the character move when swiping:
document.addEventListener('touchmove',function(e){
e.preventDefault(); //disable scroll
var board = document.getElementById(‘board);
var character = document.getElementById(‘character’);
if (e.targetTouches.length === 1) {
var touch = e.targetTouches[0];
board.classList.add(‘moving’);
character.style.left = touch.pageX + 'px';
}
}, false);
(The ‘moving’ class is used to move the background-position of the board and animate the character’s sprite in plain CSS.)
Separately, I made a little script that puts objects with random classes in a container with a set interval. These objects are then animated in css and fall from the top to the bottom of the screen.
Now, here comes the tricky part: the collision detection.
As I said, I’m new to development and vanilla JS, so I searched a bit to figure out how to detect when two objects collide, and it seems that most tutorials do this using canvases. The thing is, I’ve never used them and they scare me quite a bit. What’s more, I think it would render what I’ve done so far useless.
I’m okay with trying the canvas way, but before I do, I’d like to know if there’s any other way to detect if two moving objects collide?
Also, if there turns out to be no real way to do this without canvas, I plan on using this tutorial to learn how to build the app. However, this game wasn’t built for touchscreen devices, and the spaceship’s position changes on certain keystrokes (left & right) :
function update() {
if (keydown.left) {
player.x -= 5;
}
if (keydown.right) {
player.x += 5;
}
player.x = player.x.clamp(0, CANVAS_WIDTH - player.width);
}
My question is: how should I do to update the position using touchmove instead of keystrokes?
Thank you all in advance.
1) the idea : 'if you stop touching, you loose', is just a bad idea, drop it.
2) most convenient way to control is to handle any touch event (touch start/move/end/cancel), and to have the character align on the x coordinate of this event.
3) the intersection test is just a basic boundig box intersection check.
I made a very basic demo here, that uses touch, but also mouse to ease testing :
http://jsbin.com/depo/1/edit?js,output
a lot of optimisations are possible here, but you will see that touches adjust the ship's position, and that collisions are detected, so it will hopefully lead you to your own solution
Edit : i added default to 0 for left, top, in case they were not set.
boilerplate code :
var collisionDisplay = document.getElementById('collisionDisplay');
// hero ship
var ship = document.getElementById('ship');
ship.onload = launchWhenReady ;
// bad ship
var shipBad = document.getElementById('shipBad');
shipBad.onload = launchWhenReady ;
// image loader
imagesCount = 2 ;
function launchWhenReady() {
imagesCount --;
if (imagesCount) return;
setInterval(animate, 20);
}
var shipBadY = 0;
touch events :
// listen any touch event
document.addEventListener('touchstart', handleTouchEvent, true);
document.addEventListener('touchmove', handleTouchEvent, true);
document.addEventListener('touchend', handleTouchEvent, true);
document.addEventListener('touchcancel', handleTouchEvent, true);
// will adjust ship's x to latest touch
function handleTouchEvent(e) {
if (e.touches.length === 0 ) return;
e.preventDefault();
e.stopPropagation();
var touch = e.touches[0];
ship.style.left = (touch.pageX - ship.width / 2) + 'px';
}
animation :
// animation loop
function animate()  {
// move ship
shipBadY += 1;
shipBad.style.top = Math.ceil(shipBadY) + 'px';
// test collision
var isColliding = testCollide(shipBad);
collisionDisplay.style.display = isColliding ? 'block' : 'none';
}
collision :
// collision test when the enemy and the ship are images
function testCollide(enemi) {
var shipPosX = parseInt(ship.style.left) || 0 ;
var shipPosY = parseInt(ship.style.top) || 0 ;
var shipWidth = ship.width ;
var shipHeight = ship.height;
var badX = parseInt(enemi.style.left) || 0 ;
var badY = parseInt(enemi.style.top) || 0 ;
var badWidth = enemi.width;
var badHeight = enemi.height;
return bBoxIntersect(shipPosX, shipPosY, shipWidth, shipHeight,
badX, badY, badWidth, badHeight);
}
EDIT : in case you're not using images :
// collision test when the enemy and the ship are ** NOT ** images
function testCollide(o) {
var characterPosX = parseInt(character.style.left);
var characterPosY = parseInt(character.style.top);
var characterWidth = parseInt(character.style.width);
var characterHeight = parseInt(character.style.height);
var obstacleX = parseInt(o.style.left) || 0 ;
var obstacleY = parseInt(o.style.top) || 0 ;
var obstacleWidth = parseInt(o.style.width);
var obstacleHeight = parseInt(o.style.height);
return boundingBoxIntersect(characterPosX, characterPosY, characterWidth, characterHeight, obstacleX, obstacleY, obstacleWidth, obstacleHeight);
}
function bBoxIntersect(x1, y1, w1, h1, x2, y2, w2, h2) {
return !(x1 + w1 < x2 || x1 > x2 + w2 || y1 + h1 < y2 || y1 > y2 + w2);
}
mouse events :
// -----------------------------------------------------
// Handle mouse event for easy testing on Browser
document.addEventListener('mousemove', handleMouseEvent);
function handleMouseEvent(e) {
ship.style.left = (e.pageX - ship.width / 2) + 'px';
}

Jump Animation in JavaScript

My newest Hobby Project is a very simple Jump'n'Run Game using JavaScript. I already wrote some code (with the help of a tutorial at lostdecadegames) and read everything about the GameLoop.
var start = true;
// Create the canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = 1200;
canvas.height = 480;
document.body.appendChild(canvas);
var jumping = false;
var gravity = 1.5;
var pressed = true;
// Background image
var bgReady = false;
var bgImage = new Image();
bgImage.onload = function () {
bgReady = true;
};
bgImage.src = "background.png";
// Hero image
var heroReady = false;
var heroImage = new Image();
heroImage.onload = function () {
heroReady = true;
};
heroImage.src = "hero.png";
// Monster image
var monsterReady = false;
var monsterImage = new Image();
monsterImage.onload = function () {
monsterReady = true;
};
monsterImage.src = "monster.png";
// Game objects
var hero = {
speed_x: 50,
speed_y_up: 50,
speed_y_down: 50, // movement in pixels per second
velocity_x: 50,
velocity_y: 50
};
// Handle keyboard controls
var keysDown = {};
addEventListener("keydown", function (e) {
keysDown[e.keyCode] = true;
}, false);
addEventListener("keyup", function (e) {
delete keysDown[e.keyCode];
}, false);
// Update game objects
var update = function (modifier) {
if(38 in keysDown) { // Player holding up
jumping = true;
//hero.y -= hero.speed_y_up * modifier;
}
if (40 in keysDown) { // Player holding down
hero.y += hero.speed_y_down * modifier;
}
if (37 in keysDown) { // Player holding left
hero.x -= hero.speed_x * modifier;
}
if (39 in keysDown) { // Player holding right
hero.x += hero.speed_x * modifier;
}
};
// Draw everything
var render = function () {
if (bgReady) {
ctx.drawImage(bgImage, 0, 0);
}
if (heroReady) {
if(hero.y > 0 && hero.y < 480 && hero.x <= -32)
{
hero.x = hero.x + 1232;
ctx.drawImage(heroImage, hero.x, hero.y);
}
else if(hero.y > 0 && hero.y < 480 && hero.x >= 1200)
{
hero.x = hero.x - 1232;
ctx.drawImage(heroImage, hero.x, hero.y);
}
else if(jumping)
{
ctx.drawImage(heroImage, hero.x, hero.y-100);
jumping = false;
}
else ctx.drawImage(heroImage, hero.x, hero.y);
}
if (monsterReady) {
ctx.drawImage(monsterImage, monster.x, monster.y);
}
};
// The main game loop
var main = function () {
var now = Date.now();
var delta = now - then;
update(delta / 500);
render();
then = now;
};
// Starting the game!
reset();
var then = Date.now();
setInterval(main, 1); // Execute as fast as possible
As you can see, I already added a fix gravity var and some speed vars. The Hero moves very smooth, so this is no problem.
I have 2 problems with the jump-Animation:
The Hero stays in the air, when the Up-Key is keep being pressed. I tried to fix this with some boolean vars, but I couldn't figure it out how to get the Hero down again.
Right now, I implemented a "dirty hack" which causes the Hero to be repainted 50px higher, but I want a smooth Jump, so that the Hero gets slower while going up and speeds up while falling. I looked up so many Tutorials and so much Example Code, but I'm too stupid to figure it out, how I get my desired Animation.
Hope you guys can give me some advice for my problem (I'm not asking for the final code, I just need some tips).
It's hard to understand exactly what the if statements inside of if (heroReady) are doing because the numbers don't mean anything to me, but it seems to me like your problem is in there.
First of all, it seems to me like jumping should the first condition checked. If one of the first conditions is true, then it doesn't matter whether or not he's jumping. I can't easily tell when each condition is true, though, so I'm going to assume that when the player is holding up,
else if(jumping)
{
ctx.drawImage(heroImage, hero.x, hero.y-100);
jumping = false;
}
gets executed like normal.
Now, assuming that, I think your issue is that jumping is determined solely by whether or not the player is holding up, because as soon as jumping is true, it becomes false. This is incorrect.
Jumping should be set to true when the player presses the up key, but it should be set to false when they remove it. It should be set to false when the animation hits the ground.
Another issue that you have is the fact that you aren't actually using the hero's attributes to render its jumping location, you're simply offsetting it. Perhaps that is just your workaround until the problem is solved, but it makes it hard to tell when the character hits the ground, because you can't start lower the character (increasing the y value) after they jump, since you never raised them by decreasing the y value.
So how do we fix this?
Here are my recommendations. You might find more elegant ways to do it by the time you're done due to refactoring, but the way you have it set up right now I think it will work fine:
Set jumping as soon as they press up, like you're doing, but only if jumping == false, because presumably your hero can't do mid-air jumps.
Immediately after you set jumping (and inside the same if statement), update their velocity.
In your update section, add another if for whether or not the player is jumping, regardless of whether or not they are pressing any keys. If they are, decrease their momentum based on gravity. Then, add a check for if their momentum is the opposite of how much you increase it when they start jumping. In other words, check if they are moving down at exactly the same rate they were moving up when they started jump. This happens at exactly the y-coordinate that they began the jump from. (This is more reliable that just checking their position, because it will work from multiple y-locations.) The alternative would be to store a variable with the y-coordinate they were at when they jumped. Either way, if their jump has ended, set jumping to false.
Since you're updating their coordinates based on jumping, in your render function, you can eliminate any jumping logic and just draw the image based on the coordinates.
Does that help at all?

Best way to do smooth motion for an Image

I wrote a quick program to bounce a ball on the screen. Everything works, but the image is prone to flickering and is not smooth.
I suspect that the image flickers because the velocity is significant at the bottom of the screen.
I was wondering if anybody had any ideas on how to interpolate the motion of the ball to reduce the flicker.
Called to update the position
this.step = function()
{
thegame.myface.y = thegame.myface.y + thegame.myface.vSpeed;
if (thegame.myface.y > thegame.height)
{
thegame.myface.vSpeed = -thegame.myface.vSpeed;
}
else
{
thegame.myface.vSpeed = thegame.myface.vSpeed + 1;
}
}
},
Called to redraw
draw: function()
{
//clears the canvas
thegame.ctx.clearRect(0,0,thegame.width,thegame.height);
//draw the objects
thegame.ctx.drawImage(thegame.imgFace,this.x-this.width/2,this.y-this.height/2);
return;
},
Call framework in index.html
<script type="text/javascript">
thegame.init(450,450);
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
(function()
{
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x)
{
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelRequestAnimationFrame = window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
{
var f = function(callback, element)
{
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16-(currTime-lastTime));
var id = window.setTimeout(function()
{
callback(currTime+timeToCall);
}, timeToCall);
lastTime = currTime+timeToCall;
return id;
};
window.requestAnimationFrame = f;
}
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id)
{
clearTimeout(id);
};
}());
(function gameloop()
{
thegame.update();
requestAnimationFrame(gameloop);
thegame.draw();
})();
</script>
edit definition for thegame
init: function(width, height)
{
var canvas = $("<canvas width='"+width+"' height='"+height+"'></canvas>");
canvas.appendTo("body");
thegame.ctx = canvas.get(0).getContext("2d");
thegame.width = width;
thegame.height = height;
thegame.imgFace = new Image();
thegame.imgFace.src = "face.png";
thegame.myface = new thegame.makeFace(width/2,height/2);
},
It's about visual perception. First find out at what rate the game loop is called by the browser via requestanimationframe. If it's Chrome its task manager will help. If not, calc the rate by yourself with timestamps. It should be at least 60 times per second. If the browser is running at this rate and the movement is still not smooth, the speed is simply too high for that rate.
However, you have options to trick the perception of motion. One is to make the image smaller (simple), another is motion blur (complicated). To do the latter, you basically run the game hidden at double speed and draw two blended frames onto the visible canvas. Or at same speed and a bit simpler, keep track of the last two images and draw both with 50% alpha at the canvas. If you want more background info follow the discussion why latest hobbit movie was filmed at 48 instead of the usual 24 frames per second.
If it appears the image is horizontally chopped/cut in half then the browser is not properly synchronized to the monitor. In this case make sure the vertical sync (vsync) isn't overridden somewhere by the system or display options.
How large is your canvas? Not sure if it will solve your flicker issue, but an additional optimization you can try is to modify your clearRect() call to only clear the region which is dirty, before each update.
Ex: thegame.ctx.clearRect(this.x-this.width/2, this.y-this.height/2, thegame.imgFace.width, thegame.imgFace.height);

Can you use new Date().getMilliseconds() (or a different alternative) as a timer (like setTimeout or setInterval), and if so, how?

I have two setTimeout functions: one makes a div pop up randomly on the y axis, and one moves it vertically. These to need to be perfectly in sync, and setTimeout just doesn't cut it: the div has to reset its x position as soon as its y position becomes greater than the window height, before resetting to 0 to start anew. Something like that. I'm thinking of somehow integrating getMilliseconds, but any alternative will do.
Here's the current Javascript:
var width = window.outerWidth;
var height = window.outerHeight;
var h2 = height * 5;
//left
function LR() {
setTimeout(function() {
var left = [];
var one = 1;
do {
one++;
left.push(one);
}
while (one <= width);
var random = Math.floor(Math.random() * left.length);
document.getElementById("test").style.left = random + "px";
LR();
}, h2);
}
LR();
//top
function TB() {
setTimeout(function() {
var one = document.getElementById("test").offsetTop;
one++;
document.getElementById("test").style.top = one + "px";
if (one == height) {
one == 0;
document.getElementById("test").style.top = 0;
}
TB();
}, 2);
}
TB();
If you'd like to look at my full code, and/or preview, I've set up a JSFiddle: http://jsfiddle.net/JqVb9/. Thanks in advance.
Yes, for animations you always should check the current time - setTimeout/.Interval are absolutely unreliable and tend to drift.
To do that, just use Date.now() and make your animation a mathematical function to get the position from time (easiest: linear movement).
As #JanDvorak already pointed out in the comments, you should consider performing all movements from the same timer. This will make the animation smoother as you don't change the DOM / styling too often - each frame is usually rendered only once. Also, this will ensure synchronity.

Categories