Pong ball - high speed results in ball not colliding - Javascript - javascript

I'm making a pong ball game in javascript. When the velocity is low, the game works fine and the ball will collide with the paddle. However, when the velocity gets high, the ball will "teleport" through the paddle, because the ball's position is updated this way:
update(delta, playerPad, aiPad) {
this.x += this.direction.x * this.velocity * delta;
this.y += this.direction.y * this.velocity * delta;
}
How can I solve this issue? This caused the game to be unplayable at high speed. Below is my coding snippet
const INIT_VELOCITY = 0.3;
const INCREMENT_VELOCITY = 0.000;
const PI = Math.PI;
class Ball {
constructor(ballElem) {
this.ball = ballElem;
this.reset();
}
get x() {
return parseFloat(getComputedStyle(this.ball).getPropertyValue("--x"));
}
get y() {
return parseFloat(getComputedStyle(this.ball).getPropertyValue("--y"));
}
set x(value) {
this.ball.style.setProperty("--x", value);
}
set y(value) {
this.ball.style.setProperty("--y", value);
}
reset() {
this.x = 50;
this.y = 50;
this.velocity = INIT_VELOCITY;
//setting the direction of the ball
// let angle = Math.random() * 2 * PI;
let angle = 2*PI;
// while (
// (angle > PI / 3 && angle < (2 * PI) / 3) ||
// (angle > (4 * PI) / 3 && angle < (5 * PI) / 3)
// ) {
// angle = Math.random() * 2 * PI;
// }
this.direction = {
x: Math.cos(angle),
y: Math.sin(angle),
};
}
rect() {
return this.ball.getBoundingClientRect();
}
update(delta, playerPad, aiPad) {
this.x += this.direction.x * this.velocity * delta;
this.y += this.direction.y * this.velocity * delta;
this.velocity += INCREMENT_VELOCITY;
const rect = this.rect();
if (rect.top <= 0) {
this.direction.y *= -1;
this.y = 0 + (this.rect().height / 2 / innerHeight) * 100;
}
if (rect.bottom >= innerHeight) {
this.direction.y *= -1;
this.y = ((innerHeight - this.rect().height / 2) / innerHeight) * 100;
}
if(this.isColliding(rect,playerPad.rect())){
this.direction.x *= -1;
this.x = ( playerPad.rect().right + rect.width / 2 ) / innerWidth * 100;
}
if(this.isColliding(rect,aiPad.rect())){
this.direction.x *= -1;
this.x = ( aiPad.rect().left - rect.width / 2 ) / innerWidth * 100;
}
if (rect.left <= 0) {
this.reset();
}
if (rect.right >= innerWidth) {
this.reset();
}
}
isColliding(rect1, rect2) {
// console.log("collision");
return rect1.right >= rect2.left
&& rect1.left <= rect2.right
&& rect1.bottom >= rect2.top
&& rect1.top <= rect2.bottom
}
}
class Paddle {
constructor(padElem){
this.pad = padElem;
}
get y(){
return getComputedStyle(this.y).getPropertyValue("--y");
}
set y(value){
this.pad.style.setProperty("--y",value);
}
rect(){
return this.pad.getBoundingClientRect();
}
}
const ball = new Ball(document.querySelector("#ball"));
const playerPad = new Paddle(document.querySelector("#player-pad"));
const aiPad = new Paddle(document.querySelector("#ai-pad"));
let lastTime = 0;
function update(time){
const delta = time - lastTime;
lastTime = time;
ball.update(delta,playerPad,aiPad);
aiPad.y = ball.y;
requestAnimationFrame(update);
}
requestAnimationFrame(update);
document.addEventListener("mousemove",e=>{
playerPad.y = e.y / innerHeight * 100;
})
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--paddle-color: white;
--ball-color: white;
}
body {
background-color: black;
overflow: hidden;
}
#ball {
--x: 50;
--y: 50;
position: absolute;
top: calc(var(--y)*1vh);
left: calc(var(--x)*1vw);
transform: translate(-50%,-50%);
width: calc(1vw + 1rem);
height: calc(1vw + 1rem);
background-color: var(--ball-color);
border-radius: 50%;
}
.paddle {
--y: 50;
position: absolute;
width: 5px;
height: 15vh;
background-color: var(--paddle-color);
border-radius: 2rem;
top: calc(var(--y)*1vh);
transform: translate(0,-50%);
}
#player-pad {
left: 4px;
}
#ai-pad {
right: 4px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Pong Ball Clone</title>
<link href="./index.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="ball"></div>
<div class="paddle" id="player-pad"></div>
<div class="paddle" id="ai-pad"></div>
<script src="./script.js" type="module"></script>
</body>
</html>

Related

how do i move multipal balls

how do i add multipel ball to move plss help
here is the code for ball and how it bounces
review it
i think its the obj name thats where the problem
i cant manage to make new name on evry time a new obj
is created
i also tried jquary to add multipal ball ellement
but that also does not work
// code for ball
const INITIAL_VELOCITY = 0.085
const VELOCITY_CHANGE_RATE = 0
class Ball {
constructor(ballElem) {
this.ballElem = ballElem
this.reset()
}
get x() {
return parseFloat(getComputedStyle(this.ballElem).getPropertyValue("--x"))
}
set x(value) {
this.ballElem.style.setProperty("--x", value)
}
get y() {
return parseFloat(getComputedStyle(this.ballElem).getPropertyValue("--y"))
}
set y(value) {
this.ballElem.style.setProperty("--y", value)
}
rect() {
return this.ballElem.getBoundingClientRect()
}
reset() {
this.x = 50
this.y = 50
this.direction = {x : 0}
while (Math.abs(this.direction.x) <= 0.1 || Math.abs(this.direction.x) >= 0.9) {
const heading = randomNumberBetween(0, 2 * Math.PI)
this.direction = { x: Math.cos(heading), y: Math.sin(heading) }
}
this.velocity = INITIAL_VELOCITY
}
update(delta,index) {
console.log(delta,index)
this.x += this.direction.x * this.velocity * delta
this.y += this.direction.y * this.velocity * delta
// this.velocity += VELOCITY_CHANGE_RATE * delta
// this.velocity -= VELOCITY_CHANGE_RATE / delta
const rect = this.rect()
if (rect.bottom >= window.innerHeight || rect.top <= 0) {
this.direction.y *= -1
}
if (rect.right >= window.innerWidth || rect.left <= 0) {
this.direction.x *= -1
}
//background color
const hue = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--hue"));
document.documentElement.style.setProperty("--hue", hue + delta * 0.02)
}
}
function randomNumberBetween(min, max) {
return Math.random() * (max - min) + min
}
var ballarr = []
for(var i = 0; i <= 10; i++){
document.querySelector('body').innerHTML += `<div class="ball" id="ball${i}"></div>`
ballarr[i] = new Ball(document.querySelector(`#ball${i}`))
}
console.log(ballarr)
// const ball = new Ball(document.querySelector("#ball"))
let lastTime
function updateTime(time) {
if (lastTime != null) {
const delta = time - lastTime
ballarr.map((val, index) => {
val.update(delta, index)
})
}
lastTime = time
window.requestAnimationFrame(updateTime)
}
*,
*::after,
*::before {
box-sizing: border-box;
}
:root {
--hue: 200;
--saturation: 50%;
--foreground-color: hsl(var(--hue), var(--saturation), 75%);
--background-color: hsl(var(--hue), var(--saturation), 20%);
}
body {
margin: 0;
overflow: hidden;
}
.ball {
--x: 50;
--y: 50;
position: absolute;
/*background-color: var(--foreground-color); */
/*background-color: #13ecb6;*/
background-color: black;
/*border: 4px solid #13ecb6;*/
left: calc(var(--x) * 1vw);
top: calc(var(--y) * 1vh);
border-radius: 50%;
transform: translate(-50%, -50%);
width: 3vh;
height: 3vh;
}
<div class="ball" id="ball"></div>
The issue is due to how you create the balls and add them to the DOM and your ballarr array:
var ballarr = []
for (var i = 0; i <= 10; i++) {
const ballEl = document.createElement('div');
ballEl.classList.add('ball');
document.body.appendChild(ballEl);
const ball = new Ball(ballEl);
ballarr.push(ball);
}
You also need to use forEach() to loop through the array to update ball positions, not map():
ballarr.forEach(ball => ball.update(delta));
Here's a full working example:
// code for ball
const INITIAL_VELOCITY = 0.085
const VELOCITY_CHANGE_RATE = 0
class Ball {
constructor(ballElem) {
this.ballElem = ballElem
this.reset()
}
get x() {
return parseFloat(getComputedStyle(this.ballElem).getPropertyValue("--x"))
}
set x(value) {
this.ballElem.style.setProperty("--x", value)
}
get y() {
return parseFloat(getComputedStyle(this.ballElem).getPropertyValue("--y"))
}
set y(value) {
this.ballElem.style.setProperty("--y", value)
}
rect() {
return this.ballElem.getBoundingClientRect()
}
reset() {
this.x = 50
this.y = 50
this.direction = {
x: 0
}
while (Math.abs(this.direction.x) <= 0.1 || Math.abs(this.direction.x) >= 0.9) {
const heading = randomNumberBetween(0, 2 * Math.PI)
this.direction = {
x: Math.cos(heading),
y: Math.sin(heading)
}
}
this.velocity = INITIAL_VELOCITY
}
update(delta) {
this.x += this.direction.x * this.velocity * delta
this.y += this.direction.y * this.velocity * delta
const rect = this.rect()
if (rect.bottom >= window.innerHeight || rect.top <= 0) {
this.direction.y *= -1
}
if (rect.right >= window.innerWidth || rect.left <= 0) {
this.direction.x *= -1
}
//background color
const hue = parseFloat(getComputedStyle(document.documentElement).getPropertyValue("--hue"));
document.documentElement.style.setProperty("--hue", hue + delta * 0.02)
}
}
function randomNumberBetween(min, max) {
return Math.random() * (max - min) + min
}
var ballarr = []
for (var i = 0; i <= 10; i++) {
const ballEl = document.createElement('div');
ballEl.classList.add('ball');
document.body.appendChild(ballEl);
const ball = new Ball(ballEl);
ballarr.push(ball);
}
let lastTime
function updateTime(time) {
if (lastTime != null) {
const delta = time - lastTime
ballarr.forEach(ball => ball.update(delta));
}
lastTime = time
window.requestAnimationFrame(updateTime)
}
updateTime(lastTime);
*,
*::after,
*::before {
box-sizing: border-box;
}
:root {
--hue: 200;
--saturation: 50%;
--foreground-color: hsl(var(--hue), var(--saturation), 75%);
--background-color: hsl(var(--hue), var(--saturation), 20%);
}
body {
margin: 0;
overflow: hidden;
}
.ball {
--x: 50;
--y: 50;
position: absolute;
/*background-color: var(--foreground-color); */
/*background-color: #13ecb6;*/
background-color: black;
/*border: 4px solid #13ecb6;*/
left: calc(var(--x) * 1vw);
top: calc(var(--y) * 1vh);
border-radius: 50%;
transform: translate(-50%, -50%);
width: 3vh;
height: 3vh;
}

JS/HTML/CSS animation game -- only select animations freeze

I'm following a tutorial on using vanilla JS/HTML/CSS to create a 2D animation game. I'm currently stuck, however, because some of my "spiders" become stuck in their path, and won't move—and some of them do so out of the screen such that their webs are visible and won't disappear. I went back to the tutorial's code and tried to find any differences in the code, but couldn't find any. I've been playing around with the vertical values, as well as the part where I deal with animation frames, but couldn't fix the bug.
Could someone please help me figure out what I'm doing wrong?
document.addEventListener('DOMContentLoaded', function() {
const canvas = document.getElementById('canvas1');
const ctx = canvas.getContext('2d');
canvas.width = 500;
canvas.height = 800;
class Game {
constructor(ctx, width, height) {
this.ctx = ctx;
this.width = width;
this.height = height;
this.enemies = [];
this.enemyInterval = 400;
this.enemyTimer = 0;
this.enemyTypes = ["worm", "ghost", "spider"];
}
update(deltaTime) {
this.enemies = this.enemies.filter(object => !object.markedForDeletion);
if (this.enemyTimer > this.enemyInterval) {
this.#addNewEnemy();
this.enemyTimer = 0;
} else {
this.enemyTimer += deltaTime;
}
this.enemies.forEach(object => object.update(deltaTime));
}
draw() {
this.enemies.forEach(object => object.draw(this.ctx));
}
#addNewEnemy() {
const randomEnemy = this.enemyTypes[Math.floor(Math.random() * this.enemyTypes.length)];
if (randomEnemy === "worm") this.enemies.push(new Worm(this));
else if (randomEnemy === "ghost") this.enemies.push(new Ghost(this));
else if (randomEnemy === "spider") this.enemies.push(new Spider(this));
// this.enemies.sort(function(a, b) {
// return a.y - b.y;
// });
}
}
class Enemy {
constructor(game) {
this.game = game;
this.markedForDeletion = false;
this.frameX = 0;
this.maxFrame = 5;
this.frameInterval = 100;
this.frameTimer = 0;
}
update(deltaTime) {
this.x -= this.vx * deltaTime;
if (this.x < 0 - this.width) this.markedForDeletion = true;
if (this.frameTimer > this.frameInterval) {
if (this.frameX < this.maxFrame) this.frameX++;
else this.frameX = 0;
this.frameTimer = 0;
} else {
this.frameTimer += deltaTime;
}
}
draw(ctx) {
ctx.drawImage(this.image, this.frameX * this.spriteWidth, 0, this.spriteWidth, this.spriteHeight, this.x, this.y, this.width, this.height);
}
}
class Spider extends Enemy{
constructor() {
super(game);
this.spriteWidth = 310;
this.spriteHeight = 175;
this.width = this.spriteWidth/2;
this.height = this.spriteHeight/2;
this.x = Math.random() * this.game.width;
this.y = 0 - this.height;
this.image = spider;
this.vx = 0;
this.vy = Math.random() * 0.1 + 0.1;
this.maxLength = Math.random() * this.game.height * 0.7;
}
update(deltaTime) {
super.update(deltaTime);
if (this.y < 0 - this.height * 1.5) this.markedForDeletion = true;
this.y += this.vy * deltaTime;
if (this.y > this.maxLength) this.vy *= -1;
}
draw(ctx) {
ctx.beginPath();
ctx.moveTo(this.x + this.width/2, 0);
ctx.lineTo(this.x + this.width/2, this.y + 10);
ctx.stroke();
super.draw(ctx);
}
}
const game = new Game(ctx, canvas.width, canvas.height);
let lastTime = 1;
function animate(timeStamp) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const deltaTime = timeStamp - lastTime;
lastTime = timeStamp;
game.update(deltaTime);
game.draw();
requestAnimationFrame(animate);
}
animate(0);
})
#canvas1 {
border: 3px solid black;
width: 500px;
height: 800px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
img {
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enemy Variety</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<canvas id="canvas1"></canvas>
<img src="images/enemy_worm.png" id="worm">
<img src="images/enemy_ghost.png" id="ghost">
<img src="images/enemy_spider.png" id="spider">
<script src="script.js"></script>
</body>
</html>
Apologies for the current snippet not working. I assume it's because I can't find a way to upload the image with the code, but I'm attaching it here for reference: https://www.frankslaboratory.co.uk/downloads/enemy_spider.png (Warning: this image is only to be used for educational purposes)
In the draw() method inside the Spider class, you must call super.draw() before running other code:
draw(ctx) {
super.draw(ctx);
ctx.beginPath();
ctx.moveTo(this.x + this.width/2, 0);
ctx.lineTo(this.x + this.width/2, this.y + 10);
ctx.stroke();
}

Mapping image onto JS cloth and creating a slider

Here is all the code for the website, it has many bugs, like the footer which is stuck in the middle. So the idea is to create 11 different tissues in the format of this image mapped to the JS I have, bit i dont know how to do that.[![sample tissue][1]][1]
I would like to create a slider that functions when you click on the collection number it switchees to the next collection.
Also all the links only take the size and not the column width when hovered.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$</title>
<link rel="stylesheet" href="normalize.css">
<style type="text/css">
* {
margin: 0;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
font-family: sans-serif;
}
body {
}
#c {
display: block;
margin: 20px auto 0;
}
#info {
position: absolute;
left: -1px;
top: -1px;
width: auto;
max-width: 420px;
height: auto;
background: #f8f8f8;
border-bottom-right-radius: 10px;
border:1px solid #ccc;
}
#top {
background: #fff;
width: 100%;
height: auto;
position: relative;
border-bottom: 1px solid #eee;
}
p {
font-family: Arial, sans-serif;
color: #666;
text-align: justify;
font-size: 16px;
margin: 0px 16px;
}
#github, #twitter {
color:#3377ee;
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
margin: 0 auto;
text-align: center;
text-decoration:none;
}
.center {
text-align: center;
}
#net {
text-align:center;
white-space:nowrap;
font-size:19px;
background:rgba(0,0,0,0.1);
padding:8px 12px;
border-radius:8px;
display:block;
color:#888;
}
#net > span {
color:#3377ee;
font-family: Helvetica, Arial, sans-serif;
font-size: 14px;
display: block;
margin: 0 auto;
text-align: center;
text-decoration:none;
}
.bull {
opacity: 0.3;
margin: 0 6px;
font-size: 14px;
}
.row {
display: flex;
flex-direction: row;
flex-wrap: wrap;
border-bottom: 1px solid black;
}
.column {
flex-basis: 100%;
border-right: 1px solid black;
}
#media screen and (min-width: 800px) {
.column {
flex: 1;
}
}
#media screen and (min-width: 800px) {
._25 {
flex: 2.5;
}
._55 {
flex: 5.5;
}
._20 {
flex: 2;
}
}
a:link {
text-decoration: none;
}
a:visited {
text-decoration: none;
color: black;
}
a:hover {
text-decoration: none;
color: white;
background: black;}
a:active {
text-decoration: none;
color: white;
background: black;
}
</style>
</head>
<body>
<div class="row">
<div class="column">
Rakṣas Sari collection
</div>
<div class="column">
Concept
</div>
<div class="column">
Process
</div>
</div>
<div class="row">
<div class="column">
Red Collection N°1
</div>
<div class="column">
Collection N°2
</div>
<div class="column">
Collection N°3
</div>
<div class="column">
Collection N°4
</div>
<div class="column">
Collection N°5
</div>
<div class="column">
Collection N°6
</div>
<div class="column">
Collection N°7
</div>
<div class="column">
Collection N°8
</div>
<div class="column">
Collection N°9
</div>
<div class="column">
Collection N°10
</div>
<div class="column">
Collection N°11
</div>
</div>
<div class="row">
<div class="column _25">
Project photoshoot
</div>
<div class="column _55">
<canvas id="c"></canvas>
<div id="top">
<a id="close" href="">Reset tissue</a>
</div>
</div>
<div class="column _20">
Red is a celebratory color. It commemorates a couple’s union. It symbolizes love, sensuality, and passion. That’s why it features prominently in auspicious occasions, such as weddings, festivals, and births. As red also signifies chastity, it is the color of choice for brides.
</div>
</div>
<footer>
<div class="row">
<div class="column">
©Copyright Angelo Barbattini
</div>
<div class="column">
ECAL 2022
</div>
</div>
</footer style="position: fixed;bottom: 0;">
<!--div id="new">
Wobble some <a target="_blank" href="https://codepen.io/dissimulate/details/dJgMaO">jelly</a> <span class="bull">•</span>
Check out my <a target="_blank" href="https://www.instagram.com/abro_oks/">instagram!</a>
</div-->
<script type="text/javascript">
document.getElementById("close").onmousedown = function (e) {
e.preventDefault();
document.getElementById("info").style.display = "none";
return false;
};
// settings
var physics_accuracy = 3,
mouse_influence = 20,
mouse_cut = 5,
gravity = 1200,
cloth_height = 30,
cloth_width = 50,
start_y = 20,
spacing = 7,
tear_distance = 60;
window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
var canvas,
ctx,
cloth,
boundsx,
boundsy,
mouse = {
down: false,
button: 1,
x: 0,
y: 0,
px: 0,
py: 0
};
var Point = function (x, y) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.vx = 0;
this.vy = 0;
this.pin_x = null;
this.pin_y = null;
this.constraints = [];
};
Point.prototype.update = function (delta) {
if (mouse.down) {
var diff_x = this.x - mouse.x,
diff_y = this.y - mouse.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
if (mouse.button == 1) {
if (dist < mouse_influence) {
this.px = this.x - (mouse.x - mouse.px) * 1.8;
this.py = this.y - (mouse.y - mouse.py) * 1.8;
}
} else if (dist < mouse_cut) this.constraints = [];
}
this.add_force(0, gravity);
delta *= delta;
nx = this.x + (this.x - this.px) * 0.99 + (this.vx / 2) * delta;
ny = this.y + (this.y - this.py) * 0.99 + (this.vy / 2) * delta;
this.px = this.x;
this.py = this.y;
this.x = nx;
this.y = ny;
this.vy = this.vx = 0;
};
Point.prototype.draw = function () {
if (!this.constraints.length) return;
var i = this.constraints.length;
while (i--) this.constraints[i].draw();
};
Point.prototype.resolve_constraints = function () {
if (this.pin_x != null && this.pin_y != null) {
this.x = this.pin_x;
this.y = this.pin_y;
return;
}
var i = this.constraints.length;
while (i--) this.constraints[i].resolve();
this.x > boundsx
? (this.x = 2 * boundsx - this.x)
: 1 > this.x && (this.x = 2 - this.x);
this.y < 1
? (this.y = 2 - this.y)
: this.y > boundsy && (this.y = 2 * boundsy - this.y);
};
Point.prototype.attach = function (point) {
this.constraints.push(new Constraint(this, point));
};
Point.prototype.remove_constraint = function (constraint) {
this.constraints.splice(this.constraints.indexOf(constraint), 1);
};
Point.prototype.add_force = function (x, y) {
this.vx += x;
this.vy += y;
var round = 400;
this.vx = ~~(this.vx * round) / round;
this.vy = ~~(this.vy * round) / round;
};
Point.prototype.pin = function (pinx, piny) {
this.pin_x = pinx;
this.pin_y = piny;
};
var Constraint = function (p1, p2) {
this.p1 = p1;
this.p2 = p2;
this.length = spacing;
};
Constraint.prototype.resolve = function () {
var diff_x = this.p1.x - this.p2.x,
diff_y = this.p1.y - this.p2.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
diff = (this.length - dist) / dist;
if (dist > tear_distance) this.p1.remove_constraint(this);
var px = diff_x * diff * 0.5;
var py = diff_y * diff * 0.5;
this.p1.x += px;
this.p1.y += py;
this.p2.x -= px;
this.p2.y -= py;
};
Constraint.prototype.draw = function () {
ctx.moveTo(this.p1.x, this.p1.y);
ctx.lineTo(this.p2.x, this.p2.y);
};
var Cloth = function () {
this.points = [];
var start_x = canvas.width / 2 - (cloth_width * spacing) / 2;
for (var y = 0; y <= cloth_height; y++) {
for (var x = 0; x <= cloth_width; x++) {
var p = new Point(start_x + x * spacing, start_y + y * spacing);
x != 0 && p.attach(this.points[this.points.length - 1]);
y == 0 && p.pin(p.x, p.y);
y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)]);
this.points.push(p);
}
}
};
Cloth.prototype.update = function () {
var i = physics_accuracy;
while (i--) {
var p = this.points.length;
while (p--) this.points[p].resolve_constraints();
}
i = this.points.length;
while (i--) this.points[i].update(0.016);
};
Cloth.prototype.draw = function () {
ctx.beginPath();
var i = cloth.points.length;
while (i--) cloth.points[i].draw();
ctx.stroke();
};
function update() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
cloth.update();
cloth.draw();
requestAnimFrame(update);
}
function start() {
canvas.onmousedown = function (e) {
mouse.button = e.which;
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
(mouse.x = e.clientX - rect.left),
(mouse.y = e.clientY - rect.top),
(mouse.down = true);
e.preventDefault();
};
canvas.onmouseup = function (e) {
mouse.down = false;
e.preventDefault();
};
canvas.onmousemove = function (e) {
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
(mouse.x = e.clientX - rect.left),
(mouse.y = e.clientY - rect.top),
e.preventDefault();
};
canvas.oncontextmenu = function (e) {
e.preventDefault();
};
boundsx = canvas.width - 1;
boundsy = canvas.height - 1;
ctx.strokeStyle = "#888";
cloth = new Cloth();
update();
}
window.onload = function () {
canvas = document.getElementById("c");
ctx = canvas.getContext("2d");
canvas.width = 560;
canvas.height = 350;
start();
};
</script>
</body>
</html>```
[1]: https://i.stack.imgur.com/Celmj.jpg
The questioner is focusing on the problem of getting an actual image to look like material being moved with the wind.
The code presented to do this divides a canvas into small rectangular elements and moves each of those as required by the 'physics' given (value of gravity/wind for example).
The original just draws grid lines for each of these areas. What we need is for the equivalent rectangle in the original image to be copied to that point.
This snippet achieves this by adding a origx/y to the info kept about each point so that we know where to find the original rectangle.
It brings the image into an img element (it is important to wait until this is loaded before doing more with it) then copies it to an off-screen canvas that has the same dimensions as the one which will hold the material. This canvas is inspected when we need the 'mini image' to put at a given point.
WARNING: this code (even without the introduction of an image) is pretty processor intensive. On a farily powerful laptop with good GPU it was taking around 19% of CPU and not much less of GPU and the fan was whirring. This is even when there is no movement of the mouse. The code could do with a thorough look through, for example to stop the timer when user activity is completed, and perhaps putting the frame rate down (it's 60fps in the given code). I would not recommend it be put in a webpage and left there running - it will be a battery drainer.
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
-->
<script type="text/javascript" src=""></script>
<link rel="stylesheet" type="text/css" href="">
<style type="text/css">
body {}
.wrapper {}
* {
margin: 0;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
body {
background: #333;
}
canvas {
background: #333;
width: 100%;
height: 376px;
margin: 0 auto;
display: block;
border: solid red 2px;
}
#info {
position: absolute;
left: -1px;
top: -1px;
width: auto;
max-width: 380px;
height: auto;
background: #f2f2f2;
border-bottom-right-radius: 10px;
}
#top {
background: #fff;
width: 100%;
height: auto;
position: relative;
border-bottom: 1px solid #eee;
}
p {
font-family: Arial, sans-serif;
color: #666;
text-align: justify;
font-size: 16px;
margin: 10px;
}
a {
font-family: sans-serif;
color: #444;
text-decoration: none;
font-size: 20px;
}
#site {
float: left;
margin: 10px;
color: #38a;
border-bottom: 1px dashed #888;
}
#site:hover {
color: #7af;
}
#close {
float: right;
margin: 10px;
}
#p {
font-family: Verdana, sans-serif;
position: absolute;
right: 10px;
bottom: 10px;
color: #adf;
border: 1px dashed #555;
padding: 4px 8px;
}
</style>
<img src="https://i.stack.imgur.com/Celmj.jpg" style="margin-top: -2000px; position: absolute;">
<canvas width="1360" height="376" style="margin-top: -2000px; position: absolute;"></canvas>
<script type="text/javascript">
/*
Copyright (c) 2013 lonely-pixel.com, Stuffit at codepen.io (http://codepen.io/stuffit)
View this and others at http://lonely-pixel.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*/
const mycanvas = document.querySelector('canvas');
const mycontext = mycanvas.getContext('2d');
// settings
var physics_accuracy = 5,
mouse_influence = 20,
mouse_cut = 6,
gravity = 900,
cloth_height = 30,
cloth_width = 50,
start_y = 20,
spacing = 7,
tear_distance = 60;
window.requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
var canvas,
ctx,
cloth,
boundsx,
boundsy,
mouse = {
down: false,
button: 1,
x: 0,
y: 0,
px: 0,
py: 0
};
window.onload = function() {
// ADDED TO BRING IN THE IMAGE
mycontext.clearRect(0, 0, mycanvas.width, mycanvas.height);
mycontext.drawImage(document.querySelector('img'), 0, 0, 1180, 376);
canvas = document.getElementById('c');
ctx = canvas.getContext('2d');
canvas.width = canvas.clientWidth;
canvas.height = 376;
canvas.onmousedown = function(e) {
mouse.button = e.which;
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
mouse.down = true;
e.preventDefault();
};
canvas.onmouseup = function(e) {
mouse.down = false;
e.preventDefault();
};
canvas.onmousemove = function(e) {
mouse.px = mouse.x;
mouse.py = mouse.y;
var rect = canvas.getBoundingClientRect();
mouse.x = e.clientX - rect.left,
mouse.y = e.clientY - rect.top,
e.preventDefault();
};
canvas.oncontextmenu = function(e) {
e.preventDefault();
};
boundsx = canvas.width - 1;
boundsy = canvas.height - 1;
ctx.strokeStyle = 'rgba(222,222,222,0.6)';
ctx.strokeStyle = 'magenta';
cloth = new Cloth();
update();
};
var Point = function(x, y) {
this.x = x;
this.y = y;
this.px = x;
this.py = y;
this.vx = 0;
this.vy = 0;
this.pin_x = null;
this.pin_y = null;
this.constraints = [];
//added - remember where this point was originally so we can get the right bit of the img
this.origx = x;
this.origy = y;
};
Point.prototype.update = function(delta) {
if (mouse.down) {
var diff_x = this.x - mouse.x,
diff_y = this.y - mouse.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y);
if (mouse.button == 1) {
if (dist < mouse_influence) {
this.px = this.x - (mouse.x - mouse.px) * 1.8;
this.py = this.y - (mouse.y - mouse.py) * 1.8;
}
} else if (dist < mouse_cut) this.constraints = [];
}
this.add_force(0, gravity);
delta *= delta;
nx = this.x + ((this.x - this.px) * .99) + ((this.vx / 2) * delta);
ny = this.y + ((this.y - this.py) * .99) + ((this.vy / 2) * delta);
this.px = this.x;
this.py = this.y;
this.x = nx;
this.y = ny;
this.vy = this.vx = 0
};
Point.prototype.draw = function() {
if (this.constraints.length <= 0) return;
var i = this.constraints.length;
while (i--) this.constraints[i].draw();
};
Point.prototype.resolve_constraints = function() {
if (this.pin_x != null && this.pin_y != null) {
this.x = this.pin_x;
this.y = this.pin_y;
return;
}
var i = this.constraints.length;
while (i--) this.constraints[i].resolve();
this.x > boundsx ? this.x = 2 * boundsx - this.x : 1 > this.x && (this.x = 2 - this.x);
this.y < 1 ? this.y = 2 - this.y : this.y > boundsy && (this.y = 2 * boundsy - this.y);
};
Point.prototype.attach = function(point) {
this.constraints.push(
new Constraint(this, point)
);
};
Point.prototype.remove_constraint = function(lnk) {
var i = this.constraints.length;
while (i--)
if (this.constraints[i] == lnk) this.constraints.splice(i, 1);
};
Point.prototype.add_force = function(x, y) {
this.vx += x;
this.vy += y;
};
Point.prototype.pin = function(pinx, piny) {
this.pin_x = pinx;
this.pin_y = piny;
};
var Constraint = function(p1, p2) {
this.p1 = p1;
this.p2 = p2;
this.length = spacing;
};
Constraint.prototype.resolve = function() {
var diff_x = this.p1.x - this.p2.x,
diff_y = this.p1.y - this.p2.y,
dist = Math.sqrt(diff_x * diff_x + diff_y * diff_y),
diff = (this.length - dist) / dist;
if (dist > tear_distance) this.p1.remove_constraint(this);
var px = diff_x * diff * 0.5;
var py = diff_y * diff * 0.5;
this.p1.x += px;
this.p1.y += py;
this.p2.x -= px;
this.p2.y -= py;
};
let num = 0;
Constraint.prototype.draw = function() {
ctx.drawImage(mycanvas, this.p1.origx, this.p1.origy, spacing, spacing, this.p1.x, this.p1.y, spacing + 1, spacing + 1);
};
var Cloth = function() {
this.points = [];
var start_x = canvas.width / 2 - cloth_width * spacing / 2;
for (var y = 0; y <= cloth_height; y++) {
for (var x = 0; x <= cloth_width; x++) {
var p = new Point(start_x + x * spacing, start_y + y * spacing);
x != 0 && p.attach(this.points[this.points.length - 1]);
y == 0 && p.pin(p.x, p.y);
y != 0 && p.attach(this.points[x + (y - 1) * (cloth_width + 1)])
this.points.push(p);
}
}
};
Cloth.prototype.update = function() {
var i = physics_accuracy;
while (i--) {
var p = this.points.length;
while (p--) this.points[p].resolve_constraints();
}
i = this.points.length;
while (i--) this.points[i].update(.016);
};
Cloth.prototype.draw = function() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
var i = cloth.points.length;
while (i--) cloth.points[i].draw();
ctx.stroke();
};
function update() {
cloth.update();
cloth.draw();
requestAnimFrame(update);
}
</script>
</head>
<body>
<canvas id="c" width="800" height="376"> </canvas>
</body>
</html>
Note: it is possible to 'tear' the material with a right click and this facility probably needs removing - unless you want users to ruin the look of the cloth :)

When I render a small circle using JavaScript's getContext("2d"), it renders with a piece missing

I'm trying to make a very simple interactive segment for a webpage, and I need to render many small circles that bounce around the screen... However I am running into a very strange issue. When I render a small circle (with a radius of ~10 or less) it renders with a chunk taken out of the upper right corner for no apparent reason. If I increase the size of the circle then it begins to render properly.
Here is all of my code so far:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Nothing</title>
<style>
#header{
background: red;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 25%;
text-align: center;
}
#header h1{
position: relative;
top: 8%;
font-size: 200%;
}
#header canvas{
position: absolute;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div id = "header">
<canvas></canvas>
<h1>(Removed for privacy)</h1>
</div>
<script>
let canvas = document.getElementById("header").getElementsByTagName("canvas")[0];
canvas.width = Math.round(window.innerWidth);
canvas.height = Math.round(window.innerHeight * 0.25);
let ctx = canvas.getContext("2d");
let width = canvas.width;
let height = canvas.height;
function d2r(degrees){
return(degrees * (Math.PI / 180));
}
function random(min,max){
const diff = (max - min) + 1;
return(Math.floor(Math.random() * diff) + min);
}
</script>
<script>
class Bubble{
constructor(x,y){
this.x = x;
this.y = y;
this.r = random(0,d2r(360));
this.speed = 0.6;
this.xvel = Math.cos(this.r) * this.speed;
this.yvel = Math.sin(this.r) * this.speed;
this.size = 10;
}
draw(){
ctx.beginPath();
ctx.fillStyle = "black";
ctx.arc(this.x,this.y,this.size / 2,this.size / 2,0,d2r(360));
ctx.fill();
}
update(){
this.x += this.xvel;
this.y += this.yvel;
if(this.x < this.size / 2){
this.x = this.size / 2;
this.xvel = -this.xvel;
}
if(this.x > width - this.size / 2){
this.x = width - this.size / 2;
this.xvel = -this.xvel;
}
if(this.y < this.size / 2){
this.y = this.size / 2;
this.yvel = -this.yvel;
}
if(this.y > height - this.size / 2){
this.y = height - this.size / 2;
this.yvel = -this.yvel;
}
}
}
let bubbles = [];
for(let i = 0;i < 50;i++){
bubbles.push(new Bubble(random(4,width - 4),random(4,height - 4)));
}
</script>
<script>
let drawLoop = setInterval(function(){
ctx.fillStyle = "rgb(84, 48, 23)";
ctx.fillRect(0,0,width,height);
for(let i = 0;i < bubbles.length;i++){
bubbles[i].update();
bubbles[i].draw();
}
},1000 / 60);
</script>
</body>
</html>
You can change the this.size = 10; to different sizes to see how it looks, and when the problem comes and goes. When I set it to something tiny (like 5) it renders as almost a line.... It's very very strange.
Any help is appreciated. I assume I'm going to have to use some kind of workaround to get this working the way I want.
Edit: The code in question is in the Bubble class, inside the draw method.
Parameters for 2dArc is
void ctx.arc(x, y, radius, startAngle, endAngle [, anticlockwise]);
Inside your class member draw() you gave,
ctx.arc(this.x,this.y,this.size / 2,this.size / 2,0,d2r(360));
Among which the second this.size / 2 is not required,
removing that will fix it,
ctx.arc(this.x,this.y,this.size / 2,0,d2r(360));

after an animation, page will not reload

I'm creating a little bike dude that runs across the screen and when the mouse touches it, it is supposed to stop and look at you. its running, turning, and stopping fine, but when it stops the image of him looking at us is not working. Also, when I try to reload the page, it doesn't. I think there is a loop or something preventing it from reloading.
let speed = 5;
let lastspeed = 0;
let counter = 0;
let x = 50;
let y = 100;
let mX = 0;
let mY = 0;
//flipping and animating
function move() {
x += speed;
document.getElementById('riding').style.left=(x + "px");
if (x + document.getElementById('riding').style.width >= window.innerWidth - 100) {
speed = -5;
document.getElementById('riding').style.transform="rotateY(150deg)";
}
if (x <= 0) {
speed = 5;
document.getElementById('riding').style.transform="rotateY(0deg)";
}
if (speed == 0) {
document.getElementById('riding').src="stop.gif"; console.log('hi')
setTimeout(reset, 2000);
}
requestAnimationFrame(move);
}
//mouse move collision detection
window.addEventListener('mousemove', function(e) {
mX = e.clientX;
mY = e.clientY;
if (mX >= x && mX <= x + 100 && mY >= y && mY <= y + 100) {
lastspeed = speed;
if (counter == 0) {
slow();
counter = 1;
}
}
});
//braking it
function slow() {
document.getElementById('riding').src="brake.gif";
do {
if (speed > 0){
speed -= 0.1;
} else if(speed < 0) {
speed += 0.1;
}
}
while (speed !== 0);
}
//reset
function reset() {
document.getElementById('riding').src="riding.gif";
do {
if (lastspeed > 0) {
speed += 0.1;
} else if (lastspeed < 0) {
speed -= 0.1;
}
}
while(speed !== 5 || speed !== -5);
counter = 0;
}
move();
here is my html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h6 id="message">this is working</h6>
<img src="riding.gif" alt="riding" id="riding">
<script src="rider.js"></script>
</body>
</html>
here is my css
body{
margin: 0px;
}
#riding{
width: 50px;
height: 50px;
position: absolute;
top: 100px;
left: 0px;
transform: rotateY(0deg);
}
#message{
position: absolute;
top: 0;
left: 0;
}
let speed = 5;
let lastspeed = 0;
let counter = 0;
let x = 50;
let y = 100;
let mX = 0;
let mY = 0;
//flipping and animating
function move() {
x += speed;
document.getElementById('riding').style.left=(x + "px");
if (x + document.getElementById('riding').style.width >= window.innerWidth - 100) {
speed = -5;
document.getElementById('riding').style.transform="rotateY(150deg)";
}
if (x <= 0) {
speed = 5;
document.getElementById('riding').style.transform="rotateY(0deg)";
}
if (speed == 0) {
document.getElementById('riding').src="stop.gif"; console.log('hi')
setTimeout(reset, 2000);
}
requestAnimationFrame(move);
}
//mouse move collision detection
window.addEventListener('mousemove', function(e) {
mX = e.clientX;
mY = e.clientY;
if (mX >= x && mX <= x + 100 && mY >= y && mY <= y + 100) {
lastspeed = speed;
if (counter == 0) {
slow();
counter = 1;
}
}
});
//braking it
function slow() {
document.getElementById('riding').src="brake.gif";
do {
if (speed > 0){
speed -= 0.1;
} else if(speed < 0) {
speed += 0.1;
}
}
while (speed !== 0);
}
//reset
function reset() {
document.getElementById('riding').src="riding.gif";
do {
if (lastspeed > 0) {
speed += 0.1;
} else if (lastspeed < 0) {
speed -= 0.1;
}
}
while(speed !== 5 || speed !== -5);
counter = 0;
}
move();
body{
margin: 0px;
}
#riding{
width: 50px;
height: 50px;
position: absolute;
top: 100px;
left: 0px;
transform: rotateY(0deg);
}
#message{
position: absolute;
top: 0;
left: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h6 id="message">this is working</h6>
<img src="riding.gif" alt="riding" id="riding">
<script src="rider.js"></script>
</body>
</html>
here are the animation images I am using:
So a couple problems. Floating point error.
How to deal with floating point number precision in JavaScript?
0.1 is one of those numbers that will cause that problem.
I've gone and dealt with quick and dirty by using Math.abs and comparing difference to < 0.01.
The other problem is your use of lastSpeed. mouseover might sometimes occur at a speed of 0, which causes the loop to get stuck forever, and would cause the direction to be wrong. So I've added a || so that it will ignore 0 speed.
Your 0.1 easing should probably be placed in a rAF loop, as it is right now a blocking operation, so it doesn't function as you intend it to. And/or a timeout loop.
And an idea that you might want to consider is to use a CSS transition to do the easing slowdown for you.
let speed = 5;
let lastspeed = 0;
let counter = 0;
let x = 50;
let y = 100;
let mX = 0;
let mY = 0;
//flipping and animating
function move() {
x += speed;
document.getElementById('riding').style.left=(x + "px");
if (x + document.getElementById('riding').style.width >= window.innerWidth - 100) {
speed = -5;
document.getElementById('riding').style.transform="rotateY(150deg)";
}
if (x <= 0) {
speed = 5;
document.getElementById('riding').style.transform="rotateY(0deg)";
}
if (speed == 0) {
document.getElementById('riding').src="stop.gif";
setTimeout(reset, 2000);console.log('hi');
}
else requestAnimationFrame(move);
}
//mouse move collision detection
window.addEventListener('mousemove', function(e) {
mX = e.clientX;
mY = e.clientY;
if (mX >= x && mX <= x + 100 && mY >= y && mY <= y + 100) {
lastspeed = speed || lastspeed;
if (counter == 0) {
slow();
counter = 1;
}
}
});
//braking it
function slow() {
document.getElementById('riding').src="brake.gif";
do {
if (speed > 0){
speed -= 0.1;
} else if(speed < 0) {
speed += 0.1;
}
}
while (Math.abs(speed)>0.01);
speed=0;
}
//reset
function reset() {
document.getElementById('riding').src="riding.gif";
do {
if (lastspeed > 0) {
speed += 0.1;
} else if (lastspeed < 0) {
speed -= 0.1;
}console.log(lastspeed,speed);
}
while(5-Math.abs(speed) > 0.01);
move();
counter = 0;
}
move();
body{
margin: 0px;
}
#riding{
width: 50px;
height: 50px;
position: absolute;
top: 100px;
left: 0px;
transform: rotateY(0deg);
}
#message{
position: absolute;
top: 0;
left: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h6 id="message">this is working</h6>
<img src="riding.gif" alt="riding" id="riding">
<script src="rider.js"></script>
</body>
</html>

Categories

Resources