P5.js not working on Safari, No errors in console - javascript

I have made a simple p5.js sketch for a site I am building and some of the functions are not working on safari/ios safari, however it all works fine on chrome. There are no errors in the console.
The functions 'showAbout' and 'hideAbout' work fine on safari, but the function 'mousePressed' is not, as well as everything in the draw function. Wondering if there are any errors in my code or if this is a safari glitch?
I will paste my code below.
var dogs = [];
var index;
var x;
var y;
var angle;
var about;
var button;
var canvas;
var exit;
var toggle = false;
var w = window.innerWidth;
var h = window.innerHeight;
function preload() {
for(let i=0; i<11; i++) {
dogs[i] = loadImage(`Images/Batch/dog${i}.jpg`);
}
about = select('.about-container');
about.style('display', 'none');
}
function setup() {
canvas = createCanvas(w, h);
frameRate(5);
angleMode(DEGREES);
button = select('.about-button-text');
button.mousePressed(showAbout);
exit = select('.exit');
exit.mousePressed(hideAbout);
}
function draw() {
fill(255);
textSize(25);
angle = random(-45,45);
rotate(angle);
index = random(dogs);
index.width = w/3;
x = random(w);
y = random(h);
image(index, x, y);
}
function mousePressed() {
if (toggle) {
noLoop();
toggle = false;
} else {
loop();
toggle = true;
}
}
function showAbout() {
about.show();
}
function hideAbout() {
about.hide();
}
function windowResized() {
resizeCanvas(w, h);
}

It's hard to be 100% certain without a reproducible example but it would appear that assigning the width property of a p5.Image object causes images not to display on Safari (no errors, works fine on Chrome). Generally speaking you should not assign values to the fields of p5.js objects. I don't see anything in the documentation suggesting this would be supported. Instead what you probably want to do is specify dimensions in your call to the image() function:
// I've also taken the liberty of making the variables used here local
// to this function instead of being declared globally.
function draw() {
fill(255);
textSize(25);
let angle = random(-45,45);
rotate(angle);
let img = random(dogs);
// ***** This is broken in Safari, and not supported in general:
// img.width = w / 3;
let x = random(w);
let y = random(h);
let aspect = img.height / img.width;
// image(img, x, y);
// ***** Instead of changing the width property of the p5.Image object you
// should be specifying the width and height parameters of the image()
// function:
image(img, x, y, w / 3, (w / 3) * aspect);
}
Runnable example:
const imageUrls = [
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Forange-fish.jpg?v=1613865086898',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fblue-fish.jpg?v=1613865087591',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fgreen-fish.jpg?v=1613865088114',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fpurple-fish.jpg?v=1613865090105',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fwhite-fish.jpg?v=1613865093930',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fblack-fish.jpg?v=1613983100022',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fred-fish.png?v=1613983604942'
];
var dogs = [];
var about;
var button;
var canvas;
var exit;
var toggle = true;
var w = window.innerWidth;
var h = window.innerHeight;
function preload() {
for(let i=0; i<imageUrls.length; i++) {
let url = imageUrls[i];
dogs[i] = loadImage(imageUrls[i]);
}
about = select('.about-container');
about.style('display', 'none');
}
function setup() {
canvas = createCanvas(w, h);
frameRate(5);
angleMode(DEGREES);
button = select('.about-button-text');
button.mousePressed(showAbout);
exit = select('.exit');
exit.mousePressed(hideAbout);
}
function draw() {
fill(255);
textSize(25);
let angle = random(-45,45);
rotate(angle);
let img = random(dogs);
//img.width = w/3;
let x = random(w);
let y = random(h);
let aspect = img.height / img.width;
//image(img, x, y);
image(img, x, y, w / 3, (w / 3) * aspect);
}
function mousePressed() {
if (toggle) {
print('toggle off');
noLoop();
toggle = false;
} else {
print('toggle on');
loop();
toggle = true;
}
}
function showAbout() {
about.show();
}
function hideAbout() {
about.hide();
}
function windowResized() {
print(`Resize ${w} x ${h}`);
resizeCanvas(w, h);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
<meta charset="utf-8" />
</head>
<body>
<button class="about-button-text">Show About</button>
<div class="about-container">
<p>About info...</p>
<button class="exit">Exit</button>
</div>
</body>
</html>
Update: A version of the snippet that starts displaying images as soon as any are availalbe
const imageUrls = [
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Forange-fish.jpg?v=1613865086898',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fblue-fish.jpg?v=1613865087591',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fgreen-fish.jpg?v=1613865088114',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fpurple-fish.jpg?v=1613865090105',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fwhite-fish.jpg?v=1613865093930',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fblack-fish.jpg?v=1613983100022',
'https://cdn.glitch.com/0e291b8c-6059-4ca6-a0ae-84e67e1f94e7%2Fred-fish.png?v=1613983604942'
];
var dogs = [];
var about;
var button;
var canvas;
var exit;
var toggle = true;
var w = window.innerWidth;
var h = window.innerHeight;
function setup() {
canvas = createCanvas(w, h);
frameRate(5);
angleMode(DEGREES);
for (let i = 0; i < imageUrls.length; i++) {
loadImage(imageUrls[i], img => dogs.push(img));
}
about = select('.about-container');
about.style('display', 'none');
button = select('.about-button-text');
button.mousePressed(showAbout);
exit = select('.exit');
exit.mousePressed(hideAbout);
}
function draw() {
fill(255);
textSize(25);
let angle = random(-45, 45);
rotate(angle);
if (dogs.length > 0) {
let img = random(dogs);
//img.width = w/3;
let x = random(w);
let y = random(h);
let aspect = img.height / img.width;
//image(img, x, y);
image(img, x, y, w / 3, (w / 3) * aspect);
}
}
function mousePressed() {
if (toggle) {
print('toggle off');
noLoop();
toggle = false;
} else {
print('toggle on');
loop();
toggle = true;
}
}
function showAbout() {
about.show();
}
function hideAbout() {
about.hide();
}
function windowResized() {
print(`Resize ${w} x ${h}`);
resizeCanvas(w, h);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.js"></script>
<meta charset="utf-8" />
</head>
<body>
<button class="about-button-text">Show About</button>
<div class="about-container">
<p>About info...</p>
<button class="exit">Exit</button>
</div>
</body>
</html>

Related

JS Canvas, How to make an image slowly disappear?

Here is the code I have for now:
<!DOCTYPE html>
<html>
<head>
<style>
canvas{border:#666 2px solid; }
</style>
</head>
<body>
<canvas id="canvas1" width="1650" height="825" style="width: 650px; height: 330px;"></canvas>
<script>
var canvas,ctx,x,y,w,e;
var slikeURL = [
"https://www.a1smallbusinessmarketing.com/images/prosbo_hires.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Wiktionary_small.svg/350px-Wiktionary_small.svg.png",
"http://www.jasonjsmith.com/img/small-business-seo.jpg",
"https://g.twimg.com/business/page/image/11TwitterForSmallBusiness-300_1.png"
]
var slike = slikeURL.map(url=>{var img = new Image(); img.src = url; return img});
function start_canvas()
{
var brojac = 0;
function draw()
{
ctx.drawImage(slike[brojac], x, y);
};
function draw1(w, e)
{
ctx.drawImage(slike[brojac], w, e);
};
function update(time)
{
ctx.clearRect(0,0,canvas.width,canvas.height);
if(w >= x)
{
e += 8;
y += 8;
}
else
{
x -= 4;
w += 4;
};
if(slike){
ctx.drawImage(slike[brojac], x, y);
ctx.drawImage(slike[brojac], w, e);
}
if (e > canvas.height)
{
brojac = (brojac + 1) % slike.length;
x = canvas.width-190;
y = 15;
w = 1;
e = 15;
}
requestAnimationFrame(update)
};
canvas = document.getElementById('canvas1');
x = canvas.width-190;
y = 15;
w = 1;
e = 15;
ctx = canvas.getContext('2d');
requestAnimationFrame(update)
}
window.addEventListener("load",start_canvas);
document.write(brojac);
</script>
</body>
</html>
What I want to happen is that the last two images in the cycle start to slowly disappear (NOT fade out). Here are a few simple images of what I had in mind.
I want the two images to move towards each other like they normally move in the code, but the moment a part of an image crosses the invisible line, that part gets deleted until the whole image disappears, and then the cycle starts over.
I did try to crop the image while it is moving, but I wasn't able to make that work. Any ideas how I can make this?
Simply add an extra clearRect() for the region you want to hide when the criteria (image index) is fulfilled.
var canvas,ctx,x,y,w,e;
var slikeURL = [
"https://www.a1smallbusinessmarketing.com/images/prosbo_hires.jpg",
"https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Wiktionary_small.svg/350px-Wiktionary_small.svg.png",
"http://www.jasonjsmith.com/img/small-business-seo.jpg",
"https://g.twimg.com/business/page/image/11TwitterForSmallBusiness-300_1.png"
]
var slike = slikeURL.map(url=>{var img = new Image(); img.src = url; return img});
function start_canvas()
{
var brojac = 0;
function draw()
{
ctx.drawImage(slike[brojac], x, y);
};
function draw1(w, e)
{
ctx.drawImage(slike[brojac], w, e);
};
function update(time)
{
ctx.clearRect(0,0,canvas.width,canvas.height);
if(w >= x)
{
e += 8;
y += 8;
}
else
{
x -= 4;
w += 4;
};
if(slike){
ctx.drawImage(slike[brojac], x, y);
ctx.drawImage(slike[brojac], w, e);
if (brojac > 0) ctx.clearRect(412,0,824,ctx.canvas.height);
}
if (e > canvas.height)
{
brojac = (brojac + 1) % slike.length;
x = canvas.width-190;
y = 15;
w = 1;
e = 15;
}
requestAnimationFrame(update)
};
canvas = document.getElementById('canvas1');
x = canvas.width-190;
y = 15;
w = 1;
e = 15;
ctx = canvas.getContext('2d');
requestAnimationFrame(update)
}
window.addEventListener("load",start_canvas);
document.write(brojac);
canvas{border:#666 2px solid; }
<canvas id="canvas1" width="1650" height="825" style="width: 650px; height: 330px;"></canvas>
(adjust region as needed).

Setting up canvas background animation

I'm trying to set up a background in canvas and have some small circles just flow throughout the background, eventually I'll change the shapes and add more details in, but I'm just having trouble with the set up.
I know my code is janky, but are there any suggestions to the structure of the code?
var dx = 1;
var dy = 2;
var circle=new Circle(400,30,10);
var timer;
function Circle(x,y,r){
this.x=x;
this.y=y;
this.r=r;
}
function init() {
// Get the canvas element.
canvas = document.getElementById("canvas");
if (canvas.getContext) {
ctx = canvas.getContext("2d");
ctx.fillStyle = "black";
}
timer=setInterval(draw, 10);
return timer;
}
function gradient (){
var my_gradient=ctx.createLinearGradient(0,0,1000,0);
my_gradient.addColorStop(0,"black");
my_gradient.addColorStop(1,"white");
ctx.fillStyle=my_gradient;
ctx.fillRect(0,0,1000,1000);
ctx.rect(0, 0, 1000, 1000);
stars();
}
function stars(){
for (i = 0; i <= 50; i++) {
// Get random positions for stars
var x = Math.floor(Math.random() * 1000)
var y = Math.floor(Math.random() * 1000)
ctx.fillStyle = "yellow";
//if (x < 30 || y < 30) ctx.fillStyle = "black";
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI * 2, true);
ctx.closePath();
ctx.fill();
}
}
function move(){
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = gradient.my_gradient;
ctx.fillRect(0,0,canvas.width,canvas.height);
ctx.fillStyle = "#003300";
drawBall(circle);
if (circle.x +dx > canvas.width || circle.x +dx < 0)
dx=-dx;
if(circle.y+dy>bar.y && circle.x>bar.x && circle.x<bar.x+barImg.width)
dy=-dy;
if (circle.y +dy > canvas.height || circle.y +dy < 0)
dy=-dy;
circle.x += dx;
circle.y += dy;
}
I tried to code a working exemple. Here stars are popping up continuously.
HTML
<!DOCTYPE html>
<html>
<head>
<title>Exemple</title>
</head>
<body>
<canvas id="viewport"></canvas>
<script src='test.js'></script>
</body>
</html>
JS
var doc = document;
var canvas = doc.getElementById('viewport');
var ctx = canvas.getContext('2d');
var settings = {
area : {
height : 100,
width : 100
}
};
canvas.width = settings.area.width;
canvas.height = settings.area.height;
function draw() {
for (var i = 10; i--;) {
var x = Math.floor(Math.random() * 1000)
var y = Math.floor(Math.random() * 1000)
ctx.beginPath();
ctx.arc(x, y, 3, 0, Math.PI * 2, true);
ctx.fillStyle = "yellow";
ctx.closePath();
ctx.fill();
}
}
function gameLoop (render, element) {
var running, lastFrame = +new Date;
function loop( now ) {
// stop the loop if render returned false
if ( running !== false ) {
requestAnimationFrame( loop, element );
running = render( now - lastFrame );
lastFrame = now;
}
}
loop( lastFrame );
}
gameLoop (function (deltaT) {
draw();
}, canvas );
Here is the fiddle : https://jsfiddle.net/q4q0uLht/
----- EDIT -----
/*
Basic config
*/
var doc = document,
canvas = doc.getElementById('viewport'),
ctx = canvas.getContext('2d');
var settings = {
canvas: {
height: 200,
width: 300
}
}
canvas.height = settings.canvas.height;
canvas.width = settings.canvas.width;
canvas.style.border = '1px #000 solid';
/*
easy gets a random number, inside a range of [0, x);
*/
function getRandomNumber(x) {
return parseInt(Math.random() * x, 10);
}
/*
Checks if the obj passed in argument is still in the canvas limits
*/
function incorrectPosition(obj) {
return obj.x < 0 || obj.y < 0 || obj.x > settings.canvas.width || obj.y > settings.canvas.height;
}
/*
stars array and Star object.
*/
var stars = [];
function Star(r) {
this.x = getRandomNumber(canvas.width);
this.y = getRandomNumber(canvas.height);
this.r = r || 10;
this.move = function(dx, dy) {
this.x += dx;
this.y += dy;
};
}
/*
This function adds new stars,
calculates new coordinates of each star,
and removes them from the stars array
when they are out of the canvas limits.
*/
function update() {
var len = stars.length;
if (len < 10) {
stars.push(new Star());
}
for (var i = len; i--;) {
var star = stars[i];
star.move(1, 2);
if (incorrectPosition(star)) {
stars.splice(i, 1);
}
}
}
/*
This function clears the canvas each turn and
draws each star which is stored inside the stores array.
*/
function draw() {
ctx.clearRect(0, 0, settings.canvas.width, settings.canvas.height);
var len = stars.length;
for (var i = len; i--;) {
var star = stars[i];
ctx.beginPath();
ctx.arc(star.x, star.y, 3, 0, Math.PI * 2, true);
ctx.fillStyle = "yellow";
ctx.closePath();
ctx.fill();
}
}
// Here is the loop inside which are called functions
setInterval(loop, 33);
function loop() {
update(); // update first
draw(); // then draw
}
<!DOCTYPE html>
<html>
<head>
<title>Exemple</title>
</head>
<body>
<canvas id="viewport"></canvas>
<script src='test.js'></script>
</body>
</html>

Attaching two functions to event handler

I am working on html 5 canvas and I am trying to take two points as input from users and then made a line pass through them. For it I need to attach two functions to event handler but it isn't working. Following is my codes for JavaScript and html 5.
Also is there any way to accomplish it?
Browser used: Google chrome
JavaScript Code:
var c = document.getElementById("myCanvas");
var graph = c.getContext("2d");
var posX = 100;
var posY = 50;
var finalX = posX;
var finalY = posY;
var array = [[]];
var i = 0;
c.width = window.innerWidth;
c.height = window.innerHeight;
graph.strokeRect(100,50,1000,500)
// for making graph
while(finalY < 550)
{
finalY +=10;
graph.beginPath();
graph.moveTo(posX , finalY);
graph.lineTo(posX + 1000, finalY);
if(finalY == 300) {
graph.lineWidth = 2;
graph.strokeStyle = "#000000";
} else {
graph.strokeStyle = "#000000";
graph.lineWidth = 1;
}
graph.stroke();
}
while(finalX <1100) {
finalX += 10;
graph.beginPath();
graph.moveTo(finalX, posY);
graph.lineTo(finalX, posY + 500);
if(finalX == 600) {
graph.lineWidth = 2;
graph.strokeStyle = "#000000";
} else {
graph.lineWidth = 1;
graph.strokeStyle = "#000000";
}
graph.stroke();
}
// for making rectangle wherever the user clicks
function rect(e) {
var ctx = c.getContext("2d");
var x = e.clientX;
var y = e.clientY;
var j = 0;
ctx.fillStyle = "rgb(255,0,0)";
ctx.fillRect(x,y,5,5);
array[i][j] = x;
array [i][j+1] = y;
}
var joinPts = function() {
graph.beginPath();
graph.moveTo(array[0][0],array[0][1]);
graph.lineTo(array[1][0],array[1][1]);
graph.strokeStyle = "#000000";
graph.lineWidth = 2;
graph.stroke();
}
function add() {
i+=1;
}
function combine() {
rect();
add();
}
function initialise() {
c.addEventListener("click", function (){combine();}, false);
// c.addEventListener("click", rect , false); This way also it isn't working
// c.addEventListener("click", add , false);
}
HTML Code:
<!DOCTYPE HTML>
<html>
<head>
<title>Canvas!!</title>
</head>
<body style="background:white" onload="initialise()">
<canvas id="myCanvas" style="background:#f6f6f6"></canvas>
<button name="Reset" type="reset" onclick="window.location.reload();">Reset</button>
<button name="Join" type="submit" onclick="joinPts">Join</button>
<footer>
<script src="main.js"></script>
</footer>
</body>
</html>
You need to actually call the joinPts function in your onclick.
Replace:
onclick="joinPts"
With:
onclick="joinPts()"
Also, your combined function is lacking the e event parameter:
function combine(e) { // Accept a `e` parameter,
rect(e); // and pass it to `rect`.
add();
}
function initialise() {
c.addEventListener("click", combine, false); // No need to wrap `combine` in a function
}

HTML5 canvas and php on Chrome not working

I would love to create a fiddle for this to show but i'm using php and it won't let me use php in those so i'm hoping someone will still know whats going on!
I have a javascript that works completely fine on it's own. It is a HTML click and drag canvas. The click and drag is constrained to a circle and draws the image to the canvas when you click a button that is next to the canvas. This button calls a method that draws the image onto the canvas and makes it click and draggable. I have tested this by itself and it works beautifully. When I add a simple line of php code my click and drag canvas quits moving the image. When you click the button to draw the image on, that works, but then you can't move the image.
I am beyond confused because the php that i am using has nothing to do with what is going on in the canvas. Here is the code:
it's also important to point out that this code works fine in safari but doesn't work at all in chrome so i know it has something to do with chrome i just don't understand what the problem is.
My question is mainly, is there a way that safari loads versus chrome that would affect running javascript and php on the same page since it works fine in one browser and not the other. I just added the code so people would know what I am referring to.
Here is the PHP
<dl class="header">
<?php
$name = $_GET['id'];
if ($name=="bracelet") {
echo "<li>Design x!</li>";
}
elseif ($name=="purse") {
echo "<li>Design y!</li>";
}
elseif ($name=="ring") {
echo "<li>Design z!</li>";
}
?>
</dl>
Here is the full code
<HTML>
<HEAD>
<style>
#canvas {
border:1px solid red;
}
</style>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="styles.css">
</HEAD>
<BODY>
<dl class="header">
<?php
$name = $_GET['id'];
if ($name=="bracelet") {
echo "<li>Design x!</li>";
}
elseif ($name=="purse") {
echo "<li>Design y!</li>";
}
elseif ($name=="ring") {
echo "<li>Design z!</li>";
}
?>
</dl>
<h5>Add Images and Canvases with the buttons<br>
Click to select which image to move.<br>
Then move the mouse to desired drop location<br>
and click again to drop the image there.</h5>
<canvas id="canvas" width=300 height=300></canvas>
<input type="image" src="http://s25.postimg.org/tovdg674b/crystal_003.png" id="button1" width="35" height="20"></input>
<input type="image" src="http://s25.postimg.org/ph0l7f5or/crystal_004.png" id="button2" width="35" height="20"></input>
<input type="image" src="http://s25.postimg.org/60fvkwakr/crystal_005.png" id="button3" width="35" height="20"></input>
<input type="image" src="http://s25.postimg.org/fz5fl49e3/crystal_006.png" id="button4" width="35" height="20"></input>
<button id="save">save</button>
<br>
<script>
// canvas stuff
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
var radius = 50;
var contexts = [];
var points = [];
// image stuff
var states = [];
var img = new Image();
img.onload = function () {}
img.src = "http://s25.postimg.org/5qs46n4az/crystal_009.png";
setUpCanvas();
setUpPoints();
function setUpCanvas() {
contexts.push(canvas.getContext("2d"));
// link the new canvas to its context in the contexts[] array
canvas.contextIndex = contexts.length;
// wire up the click handler
canvas.onclick = function (e) {
handleClick(e, this.contextIndex);
};
// wire up the mousemove handler
canvas.onmousemove = function (e) {
handleMousemove(e, this.contextIndex);
};
canvas.addEventListener('dblclick', function() {
removeState(this.contextIndex);
});
}
function setUpPoints() {
//points that make up a circle circumference to an array
points = [];
for (var degree=0; degree<360; degree++) {
var radians = degree * Math.PI/180;
var TO_RADIANS = Math.PI/180;
var xpoint = centerX + radius * Math.cos(radians);
var ypoint = centerY + radius * Math.sin(radians);
points.push({
x: xpoint,
y: ypoint
});
}
ctx.beginPath();
ctx.moveTo(points[0].x + 4, points[0].y + 4)
//draws the thin line on the canvas
for (var i = 1; i < points.length; i++) {
var pt = points[i];
ctx.lineTo(pt.x + 4, pt.y + 4);
}
ctx.stroke(); //end of drawing the thin line
}
function addCircle() {
ctx.beginPath();
ctx.moveTo(points[0].x + 4, points[0].y + 4)
//draws the thin line on the canvas
for (var i = 1; i < points.length; i++) {
var pt = points[i];
ctx.lineTo(pt.x + 4, pt.y + 4);
}
ctx.stroke(); //end of drawing the thin line
}
function clearAll() {
//Clear all canvases
for (var i = 0; i < contexts.length; i++) {
var context = contexts[i];
context.clearRect(0, 0, canvas.width, canvas.height);
}
}
function handleClick(e, contextIndex) {
e.stopPropagation();
var mouseX = parseInt(e.clientX - e.target.offsetLeft);
var mouseY = parseInt(e.clientY - e.target.offsetTop);
for (var i = 0; i < states.length; i++) {
var state = states[i];
console.log(state);
if (state.dragging) {
state.dragging = false;
state.draw();
continue;
}
if (state.contextIndex == contextIndex && mouseX > state.x && mouseX < state.x + state.width && mouseY > state.y && mouseY < state.y + state.height) {
state.dragging = true;
state.offsetX = mouseX - state.x;
state.offsetY = mouseY - state.y;
state.contextIndex = contextIndex;
}
state.draw();
}
}
function handleMousemove(e, contextIndex) {
e.stopPropagation();
var mouseX = parseInt(e.clientX - e.target.offsetLeft);
var mouseY = parseInt(e.clientY - e.target.offsetTop);
clearAll();
addCircle();
var minDistance = 1000;
var minPoint = -1;
for (var i = 0; i < states.length; i++) {
var state = states[i];
if (state.dragging) {
for (var i = 0; i < points.length; i++) {
var pt = points[i];
var dx = mouseX - pt.x;
var dy = mouseY - pt.y;
if ((dx > 0 && dx>120)) {
state.x = mouseX - state.offsetX;
state.y = mouseY - state.offsetY;
state.contextIndex = contextIndex;
} else if ((dx < 0 && dx < -120)) {
state.x = mouseX - state.offsetX;
state.y = mouseY - state.offsetY;
state.contextIndex = contextIndex;
}
else {
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
//points in relation to the constrained line (where it will be drawn to)
//reset state.x and state.y to closest point on the line
state.x = pt.x - img.width / 2;
state.y = pt.y - img.height / 2;
state.contextIndex = contextIndex;
}
}
}
}
state.draw();
}
}
function removeState(contextIndex) {
for (var i = 0; i < states.length; i++) {
var state = states[i];
state.remove();
}
}
function addState(image) {
var ptxy = points[1];
state = {}
state.dragging = false;
state.contextIndex = 1;
state.image = image;
state.x = ptxy.x - image.width / 2;
state.y = ptxy.y - image.height / 2;
state.width = image.width;
state.height = image.height;
state.offsetX = 0;
state.offsetY = 0;
state.draw = function () {
var context = contexts[this.contextIndex - 1];
if (this.dragging) {
context.strokeStyle = 'black';
context.strokeRect(this.x, this.y, this.width + 2, this.height + 2)
}
context.drawImage(this.image, this.x, this.y);
}
state.draw();
return (state);
}
function save() {
// var data = ctx.getImageData(0, 0, canvas.width, canvas.height);
}
$("#button1").click(function () {
states.push(addState(img));
});
$("#button2").click(function () {
states.push(addState(img));
});
$("#button3").click(function () {
states.push(addState(img));
});
$("#button4").click(function () {
states.push(addState(img));
});
$("#save").click(function () {
save();
});
</script>
</BODY>
</HTML>
Anyone curious and wanting to know the answer of how i solved this here you go. I am new to HTML5 canvas and how it works. After a lot of trial and error I found out that the canvas offset was wrong once the canvas changed from the top of the screen to somewhere else. It was as simple as that....

Simple memory game doesn't update its state. Bad game loop?

I'm trying to make a simple (or so I thought) memory game. Unfortunately it does not update state of cards when user clicks on them. I'm running out of ideas, probably because it's my first javascript game. I suppose there is a problem with game loop. Could anyone at least point me in the right direction and help me understand what needs to be changed/rewritten?
//HTML5 Memory Game implementation
//main variables
var cards = [1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8];
var exposed = [makeArray("false",16)];
var first_card = 0;
var second_card = 0;
var moves = 0;
var WIDTH = 800;
var HEIGHT = 100;
var state = 0;
var mouseX = 0;
var mouseY = 0;
//creating canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.getElementById("game").appendChild(canvas);
//filling empty array with number,character,object
function makeArray(value, length) {
var newArray = [];
var i = 0;
while (i<length) {
newArray[i] = value;
i++;
}
return newArray;
}
//shuffling algorithm
function shuffle(array) {
var copy = [];
var n = array.length;
var i;
while (n) {
i = Math.floor(Math.random() * n--);
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
//where user clicks
function getClickPosition(event) {
var X = event.pageX - canvas.offsetLeft;
var Y = event.pageY - canvas.offsetTop;
return mouse = [X, Y];
}
//read click position
function readPos(event) {
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
}
//initializing
function init() {
state = 0;
moves = 0;
exposed = [makeArray("false",16)];
cards = shuffle(cards);
}
//drawing cards
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in cards) {
if (exposed[i] === true) {
ctx.fillStyle = "rgb(250, 250, 250)";
ctx.font = "50px Courier New";
ctx.fillText(cards[i], (i*50+12), 65);
} else {
ctx.strokeStyle = "rgb(250, 0, 0)";
ctx.fillStyle = "rgb(0, 0, 250)";
ctx.fillRect(i*50, 0, 50, 100);
ctx.strokeRect(i*50, 0, 50, 100);
}
}
};
//update cards
function update() {
if (exposed[parseInt(mouseX / 50)] === false) {
if (state == 0) {
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else if (state == 1) {
state = 2;
second_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else {
if (cards[first_card] != cards[second_card]) {
exposed[first_card] = false;
exposed[second_card] = false;
}
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
}
}
}
addEventListener('click', readPos, false);
setInterval(function() {
update();
draw();
}, 16);
I would check your addEventListener method: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
I also recommend you look into using jQuery.
After copy and pasting your code I found a couple of things:
You didn't add an event listener to anything, you should add it to something so I added it to document.
You initialize the exposed array with values "false" and later check if they are false. These are not the same, the string "false" isn't the Boolean false.
You initializes the exposed array as a multi dimensional array [[false,false,false ...]] this should be a single dimension array because later you check exposed[1] (1 depending on the mouse x position.
No need to call draw and update every 16 milliseconds, you can call it after someone clicked.
Wrapped the whole thing up in a function so there are no global variables created.
Here is the code after changing these obvious errors. There might be room for optimization but for now I've gotten the problems out.
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<div id="game"></div>
<script type="text/javascript">
(function(){
//HTML5 Memory Game implementation
//main variables
var cards = [1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8];
var exposed = makeArray(false, 16);
var first_card = 0;
var second_card = 0;
var moves = 0;
var WIDTH = 800;
var HEIGHT = 100;
var state = 0;
var mouseX = 0;
var mouseY = 0;
//creating canvas
var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
canvas.width = WIDTH;
canvas.height = HEIGHT;
document.getElementById("game").appendChild(canvas);
//filling empty array with number,character,object
function makeArray(value, length) {
var newArray = [];
var i = 0;
while (i < length) {
newArray.push(value);
i++;
}
return newArray;
}
//shuffling algorithm
function shuffle(array) {
var copy = [];
var n = array.length;
var i;
while (n) {
i = Math.floor(Math.random() * n--);
copy.push(array.splice(i, 1)[0]);
}
return copy;
}
//where user clicks
function getClickPosition(event) {
var X = event.pageX - canvas.offsetLeft;
var Y = event.pageY - canvas.offsetTop;
return mouse = [X, Y];
}
//read click position
function readPos(event) {
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
update();
draw();
}
//initializing
function init() {
state = 0;
moves = 0;
exposed = makeArray(false, 16);
cards = shuffle(cards);
}
//drawing cards
function draw() {
ctx.clearRect(0, 0, WIDTH, HEIGHT);
for (var i in cards) {
if (exposed[i] === true) {
ctx.fillStyle = "rgb(150, 150, 150)";
ctx.font = "50px Courier New";
ctx.fillText(cards[i], (i * 50 + 12), 65);
} else {
ctx.strokeStyle = "rgb(250, 0, 0)";
ctx.fillStyle = "rgb(0, 0, 250)";
ctx.fillRect(i * 50, 0, 50, 100);
ctx.strokeRect(i * 50, 0, 50, 100);
}
}
};
//update cards
function update() {
if (exposed[parseInt(mouseX / 50)] === false) {
if (state == 0) {
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else if (state == 1) {
state = 2;
second_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
} else {
if (cards[first_card] != cards[second_card]) {
exposed[first_card] = false;
exposed[second_card] = false;
}
state = 1;
first_card = parseInt(mouseX / 50);
exposed[parseInt(mouseX / 50)] = true;
}
}
}
document.body.addEventListener('click', readPos, false);
init();
draw();
})();
</script>
</body>
</html>
Your overall logic was good.
The point that was 'bad' was the way you handle the event :
the event handler should store some valuable information that
the update will later process and clear.
Here you mix your update with event handling, which cannot work
especially since the event will not fire on every update.
So i did a little fiddle to show you, the main change is
the click event handler, which update the var last_clicked_card :
http://jsfiddle.net/wpymH/
//read click position
function readPos(event) {
last_clicked_card = -1;
mousePos = getClickPosition(event);
mouseX = mousePos[0];
mouseY = mousePos[1];
// on canvas ?
if ((mouseY>100)||(mouseX<0)||(mouseX>WIDTH)) return;
// now yes : which card clicked ?
last_clicked_card = Math.floor(mouseX/50);
}
and then update is the processing of this information :
//update cards
function update() {
// return if no new card clicked
if (last_clicked_card == -1) return;
// read and clear last card clicked
var newCard = last_clicked_card;
last_clicked_card=-1;
// flip, store it as first card and return
// if there was no card flipped
if (state==0) { exposed[newCard] = true;
first_card = newCard;
state = 1 ;
return; }
// just unflip card if card was flipped
if ((state = 1) && exposed[newCard]) {
exposed[newCard]=false ;
state=0;
return;
}
// we have a second card now
second_card = newCard;
exposed[second_card] = true;
draw();
// ... i don't know what you want to do ...
if (cards[first_card] == cards[second_card]) {
alert('win'); }
else {
alert('loose'); }
exposed[first_card]=false;
exposed[second_card]=false;
state=0;
}

Categories