The script below draws 4 curved rectangles and detects if they are hovered over or clicked on with mouseup and mousemove event listeners. When one of the rectangles is hovered over, I would like it to grow slightly over the course of a second - it should then shrink back when the cursor moves off the rectangle.
The problem I have is that the only way I can think of doing this is by writing a function inside the if (this.selected) { clause which plots a new version of the rectangle for each frame in an animation lasting a second, but this clause is inside the function that plots a new version of the rectangle itself! How, then, can I write a function outside of curvedRect.prototype.makeCurvedRect which only runs when the cursor is over one of the rectangles? Any help will be appreciated.
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
var curvedRect = function(id, x, y, w, h) {
this.id = id;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.selected = false;
}
curvedRect.prototype.makeCurvedRect = function() {
ctx.beginPath();
ctx.lineWidth='8';
ctx.strokeStyle='white';
ctx.moveTo(this.x+10, this.y);
ctx.lineTo(this.x+this.w-10, this.y);
ctx.quadraticCurveTo(this.x+this.w, this.y, this.x+this.w, this.y+10);
ctx.lineTo(this.x+this.w, this.y+this.h-10);
ctx.quadraticCurveTo(this.x+this.w, this.y+this.h, this.x+this.w-10, this.y+this.h);
ctx.lineTo(this.x+10, this.y+this.h);
ctx.quadraticCurveTo(this.x, this.y+this.h, this.x, this.y+this.h-10);
ctx.lineTo(this.x, this.y+10);
ctx.quadraticCurveTo(this.x, this.y, this.x+10, this.y);
ctx.stroke();
if (this.selected) {
// BM67 edit By Blindman67 removed annoying alert and replaced it with console.log
// As I did not want to add elsewhere to the code to declare hoverMessageShown is safely declared here to stop the console repeating the message.
if(window["hoverMessageShown"] === undefined){
window["hoverMessageShown"] = true;
console.log('When hovered over, I would like this box to grow slightly over a short period of time (say a second). When the mouse is removed I would like it to shrink back.')
}
// BM67 end.
}
}
curvedRect.prototype.hitTest = function(x, y) {
return (x >= this.x) && (x <= (this.w+this.x)) && (y >= this.y) && (y <= (this.h+this.y));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
this.element.w = this.element.w;
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].makeCurvedRect();
}
}
Paint.prototype.setSelected = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].selected = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var paint = new Paint(c);
var img1 = new curvedRect('1', 200, 55, 150, 150);
var img2 = new curvedRect('2', 375, 55, 150, 150);
var img3 = new curvedRect('3', 200, 230, 150, 150);
var img4 = new curvedRect('4', 375, 230, 150, 150);
paint.addShape(img1);
paint.addShape(img2);
paint.addShape(img3);
paint.addShape(img4);
paint.render();
function mouseUp(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
if (shape) {
alert(shape.id);
}
// console.log('selected shape: ', shape);
}
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setSelected(shape);
}
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
display: block;
margin: 1em auto;
border: 1px solid black;
background: #FF9900;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='game.css' media='screen'></style>
</head>
<body>
<canvas id="game" width = "750" height = "500"></canvas>
</body>
</html>
Change the makeCurvedRect method to something like this:
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
var curvedRect = function(id, x, y, w, h) {
this.id = id;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.selected = false;
}
curvedRect.prototype.makeCurvedRect = function() {
var delta = this.selected?10:0 ;
var x = this.x - delta;
var y = this.y - delta;
var w = this.w + (2*delta);
var h = this.h + (2*delta);
ctx.beginPath();
ctx.lineWidth='8';
ctx.strokeStyle='white';
ctx.moveTo(x+10, y);
ctx.lineTo(x+ w -10, y);
ctx.quadraticCurveTo(x + w, y,x + w,y + 10);
ctx.lineTo( x + w, y + h-10);
ctx.quadraticCurveTo( x + w, y + h, x + w - 10, y + h);
ctx.lineTo(x + 10,y + h);
ctx.quadraticCurveTo( x, y + h, x, y+h-10);
ctx.lineTo(x, y+10);
ctx.quadraticCurveTo(x, y, x+10, y);
ctx.stroke();
}
curvedRect.prototype.hitTest = function(x, y) {
return (x >= this.x) && (x <= (this.w+this.x)) && (y >= this.y) && (y <= (this.h+this.y));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
this.element.width = this.element.width;
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].makeCurvedRect();
}
}
Paint.prototype.setSelected = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].selected = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var paint = new Paint(c);
var img1 = new curvedRect('1', 200, 55, 150, 150);
var img2 = new curvedRect('2', 375, 55, 150, 150);
var img3 = new curvedRect('3', 200, 230, 150, 150);
var img4 = new curvedRect('4', 375, 230, 150, 150);
paint.addShape(img1);
paint.addShape(img2);
paint.addShape(img3);
paint.addShape(img4);
paint.render();
function mouseUp(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
if (shape) {
alert(shape.id);
}
// console.log('selected shape: ', shape);
}
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setSelected(shape);
}
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
display: block;
margin: 1em auto;
border: 1px solid black;
background: #FF9900;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='game.css' media='screen'></style>
</head>
<body>
<canvas id="game" width = "750" height = "500"></canvas>
</body>
</html>
Related
Been having issues with this for a couple days, not sure why the text I'm rendering on the canvas is flashing so much. I'm using the requestAnimationFrame() function, but its still flickering.
What I want to have happen is for the text to move smoothly and they remove themselves from the array when they move completely off screen.
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var w = canvas.width;
var h = canvas.height;
var texts = [];
window.addEventListener("load", init);
function init() {
draw();
mainLoop();
}
function mainLoop() {
texts.push(createText("wow", "blue"));
texts.push(createText("wow", "green"));
texts.push(createText("wow", "red"));
setTimeout(function() { mainLoop(); }, 100);
}
function Text(x, y, vx, vy, varText, theColor){
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.color = theColor;
this.draw = function() {
drawStroked(varText, this.x, this.y, this.color);
}
}
function drawStroked(text, x, y, color) {
c.font = "30px bold Comic Sans MS";
c.strokeStyle = 'black';
c.lineWidth = 8;
c.strokeText(text, x, y);
c.fillStyle = color;
c.fillText(text, x, y);
}
function createText(varText, color) {
var x = (Math.random() * w / 2 ) + w/4;
var y = (Math.random() * h / 2) + h/2;
var vx = (Math.random() * .5) - .25
var vy = -(Math.random() * 3) - 1
return new Text(x, y, vx, vy, varText, color);
}
function draw() {
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
for(var i = 0;i < texts.length; i++) {
var currentText = texts[i];
currentText.x += currentText.vx;
currentText.y += currentText.vy;
currentText.draw();
if(currentText.x>w||currentText.x<0||currentText.y<10){
texts.splice(i, 1);
}
}
requestAnimationFrame(draw);
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<title>Game Screen</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
The flickering you are seeing results from your looping code, where you skip elements of the array when deleting elements (Element 3 needs to be deleted? You call splice(3, 1) and then continue with the loop at index 4. Since the array shifts when you call splice, you should process element 3 again).
The imho easiest way to fix this is to iterate backwards over the array. Please note that iterating backwards is less CPU cache efficient (because every array access leads to a cache miss), so another fix would be
if(currentText.x>w||currentText.x<0||currentText.y<10){
texts.splice(i--, 1); // decrement i after accessing and deleting
}
var canvas = document.getElementById("myCanvas");
var c = canvas.getContext('2d');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var w = canvas.width;
var h = canvas.height;
var texts = [];
window.addEventListener("load", init);
function init() {
draw();
mainLoop();
}
function mainLoop() {
texts.push(createText("wow", "blue"));
texts.push(createText("wow", "green"));
texts.push(createText("wow", "red"));
setTimeout(function() { mainLoop(); }, 100);
}
function Text(x, y, vx, vy, varText, theColor){
this.x = x;
this.y = y;
this.vx = vx;
this.vy = vy;
this.color = theColor;
this.draw = function() {
drawStroked(varText, this.x, this.y, this.color);
}
}
function drawStroked(text, x, y, color) {
c.font = "30px bold Comic Sans MS";
c.strokeStyle = 'black';
c.lineWidth = 8;
c.strokeText(text, x, y);
c.fillStyle = color;
c.fillText(text, x, y);
}
function createText(varText, color) {
var x = (Math.random() * w / 2 ) + w/4;
var y = (Math.random() * h / 2) + h/2;
var vx = (Math.random() * .5) - .25
var vy = -(Math.random() * 3) - 1
return new Text(x, y, vx, vy, varText, color);
}
function draw() {
c.clearRect(0, 0, c.canvas.width, c.canvas.height);
for(var i = texts.length - 1;i >= 0; i--) {
var currentText = texts[i];
currentText.x += currentText.vx;
currentText.y += currentText.vy;
currentText.draw();
if(currentText.x>w||currentText.x<0||currentText.y<10){
texts.splice(i, 1);
}
}
requestAnimationFrame(draw);
}
body {
margin: 0;
padding: 0;
overflow: hidden;
}
<!DOCTYPE html>
<html>
<head>
<title>Game Screen</title>
</head>
<body>
<canvas id="myCanvas"></canvas>
</body>
</html>
I'm having a canvas on which i have created dynamically some rectangles and when you hover over them i need to show an username that's stored, i tried to do it like this but i didn't got the result that i desired. The rectangles are not html elements so i can't use classes or ids.
This is the code that i tried:
canvas.addEventListener('mouseover', (evt)=>{
mousePos = onMousePos(canvas,evt);
for(let i=0;i<rectArray.length;i++)
if(ctx.isPointInPath(mousePos.x,mousePos.y))
console.log(rectArray[i].username);
});
The onMousePos function checks the that the mouse is inside of an hexagon and i use the same function for mouseup, mousedown and mousemove and it works.
EDIT the onMousePos is this:
function onMousePos(canvas, evt) {
const rect = canvas.getBoundingClientRect();
if (history && timeline) {
return {
x: Math.round(evt.clientX - rect.left) * 1.18,
y: Math.round(evt.clientY - rect.top) * 1.05
};
}
if (timeline && history === false) {
return {
x: Math.round(evt.clientX - rect.left),
y: Math.round(evt.clientY - rect.top) * 1.05
};
}
if (history && timeline === false) {
return {
x: Math.round(evt.clientX - rect.left) * 1.18,
y: Math.round(evt.clientY - rect.top)
};
}
return {
x: Math.round(evt.clientX - rect.left),
y: Math.round(evt.clientY - rect.top)
};
}
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: auto;
border: solid 1px white;
border-radius: 10px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
void function() {
"use strict";
// Classes
/*
Constructor function
when called with new, everything attached to the 'this' keyword
becomes a new member of the new object
*/
function ToolTip(text) {
this.text = text;
}
/*
Constructor prototype, a collection
of values & functions that are shared across all instances,
use for constant values and member functions
*/
ToolTip.prototype = {
TEXT_SIZE: 15,
TEXT_FONT: "15px Arial",
TEXT_COLOUR: "#FFFFFFFF",
BOX_BORDER_COLOUR: "#000000FF",
BOX_BACKGROUND_COLOUR: "#990000FF",
render: function(ctx,x,y) {
ctx.fillStyle = this.BOX_BACKGROUND_COLOUR;
ctx.strokeStyle = this.BOX_BORDER_COLOUR;
ctx.font = this.TEXT_FONT;
ctx.beginPath();
ctx.rect(
x,
y - this.TEXT_SIZE,
ctx.measureText(this.text).width,
this.TEXT_SIZE
);
ctx.fill();
ctx.stroke();
ctx.fillStyle = this.TEXT_COLOUR;
ctx.fillText(this.text,x,y - 2);
}
};
function Rectangle(x,y,width,height,name) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.tooltip = new ToolTip(name);
}
Rectangle.prototype = {
BORDER_COLOUR: "#000000FF",
BACKGROUND_COLOR: "#0000AAFF",
contains: function(x,y) {
return x > this.x && x < this.x + this.width
&& y > this.y && y < this.y + this.height;
},
render: function(ctx) {
ctx.strokeStyle = this.BORDER_COLOUR;
ctx.fillStyle = this.BACKGROUND_COLOR;
ctx.beginPath();
ctx.rect(this.x,this.y,this.width,this.height);
ctx.fill();
ctx.stroke();
}
};
// Variables
var canvasWidth = 150;
var canvasHeight = 150;
var canvas = null;
var ctx = null;
var rectangles = null;
// Functions
function onMouseMove(e) {
var bounds = canvas.getBoundingClientRect();
var x = e.clientX - bounds.left;
var y = e.clientY - bounds.top;
draw();
for (var i = 0; i < rectangles.length; ++i) {
var rectangle = rectangles[i];
if (rectangle.contains(x,y)) {
rectangle.tooltip.render(ctx,x,y);
return;
}
}
}
function draw() {
ctx.fillStyle = "gray";
ctx.fillRect(0,0,canvasWidth,canvasHeight);
for (var i = 0; i < rectangles.length; ++i) {
rectangles[i].render(ctx);
}
}
// Entry Point
onload = function() {
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
canvas.onmousemove = onMouseMove;
ctx = canvas.getContext("2d");
rectangles = [
new Rectangle(10,10,25,25,"User 1"),
new Rectangle(45,10,25,25,"User 2"),
new Rectangle(80,10,25,25,"User 3"),
new Rectangle(115,10,25,25,"User 4"),
new Rectangle(10,45,25,25,"User 5"),
new Rectangle(45,45,25,25,"User 6"),
new Rectangle(80,45,25,25,"User 7"),
new Rectangle(115,45,25,25,"User 8"),
new Rectangle(10,80,25,25,"User 9"),
new Rectangle(45,80,25,25,"User 10"),
new Rectangle(80,80,25,25,"User 11"),
new Rectangle(115,80,25,25,"User 12"),
new Rectangle(10,115,25,25,"User 13"),
new Rectangle(45,115,25,25,"User 14"),
new Rectangle(80,115,25,25,"User 15"),
new Rectangle(115,115,25,25,"User 16")
];
draw();
}
}();
</script>
</body>
</html>
The attached code shows how to select (on click) a drawn object on canvas and then the object is moved with a double click to click position or deselected with a double click prior to/ post-movement.
I have tried for days but could not work out how to apply this function to all objects in a class or an array via looping (via class constructor + prototyping). I would like to be able to select or deselect any object on screen.
Help will be very much appreciated. Thank you.
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
canvas {
display: block;
margin: 0px;
}
body {
margin: 0px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<input id="click1" type ="button" value="x" style="position: fixed; top: 0px; left: 650px; position: absolute;"></input>
<input id="click2" type ="button" value="y" style="position: fixed; top: 0px; left: 750px; position: absolute;"></input>
<script>
window.onload = function(){
var canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
let strokeColor;
let color;
let mouse_x;
let mouse_y;
let x;
let y;
let w;
let h;
let selected = false;
x = 50;
y = 50;
w = 50;
h = 50;
color="green";
strokeColor = "green";
document.getElementById('canvas').addEventListener("mousemove",go);
document.getElementById('canvas').addEventListener("mouseup",mouseUp);
document.getElementById('canvas').addEventListener("dblclick",dblClick);
document.getElementById('canvas').addEventListener("dblclick",move);
function move(){
if(selected == true){
x = mouse_x;
y = mouse_y;
}
}
function mouseUp(){
if(mouse_x > x && mouse_x < x+w && mouse_y > y && mouse_y < y+w){
strokeColor = "black";
selected = true;
console.log(selected);
}
}
function dblClick(){
if(mouse_x > x && mouse_x < x+w && mouse_y > y && mouse_y < y+w){
color = "green";
strokeColor = color;
selected = false;
console.log(selected);
}
}
function go(e){
mouse_x = e.clientX;
mouse_y = e.clientY;
document.getElementById('click1').value = mouse_x;
document.getElementById('click2').value = mouse_y;
}
function draw(){
context.strokeStyle = strokeColor;;
context.fillStyle = color;
context.beginPath();
context.lineWidth = 3;
context.rect(x,y,w,h);
context.fill();
context.stroke();
}
function animate(){
context.clearRect(0,0,width,height);
context.save();
draw();
context.restore();
requestAnimationFrame(animate);
}
animate();
};
</script>
</body>
</html>
This should be easy to do, let's first talk about the logic that we need.
Have a list of items that we can draw.
Make those items have their own state.
Check if when we click we hit an item or not.
Move or toggle the selected item.
Handle Clear & Draw of the canvas.
Made this simple fiddle just to show you the idea behind it,
let context = $("canvas")[0].getContext("2d");
let elements = [
new element(20, 20, 20, 20, "green"),
new element(45, 30, 30, 30, "red"),
new element(80, 40, 50, 50, "blue"),
];
let mousePosition = {
x: 0,
y: 0,
};
let selected;
function element(x, y, height, width, color) {
this.x = x;
this.y = y;
this.height = height;
this.width = width;
this.color = color;
this.selected = false;
this.draw = function(context) {
context.strokeStyle = (this.selected ? "black" : "white");
context.fillStyle = this.color;
context.beginPath();
context.lineWidth = 2;
context.rect(this.x, this.y, this.height, this.width);
context.fill();
context.stroke();
}
this.move = function(x, y) {
this.x = x;
this.y = y;
}
}
//Select Function
function get_select(x, y) {
let found;
$.each(elements, (i, element) => {
if(x > element.x
&& x < element.x + element.width
&& y > element.y
&& y < element.y + element.height) {
found = element;
}
});
return (found);
}
// Handle selection & Movement.
$("canvas").click(function() {
let found = get_select(mousePosition.x, mousePosition.y);
Clear();
// Toggle Selection
if (found && !selected) {
found.selected = true;
selected = found;
} else if (found === selected) {
found.selected = false;
selected = null;
}
// Move
if (!found && selected) {
selected.move(mousePosition.x, mousePosition.y);
}
Draw();
});
// Record mouse position.
$("canvas").mousemove((event) => {
mousePosition.x = event.pageX;
mousePosition.y = event.pageY;
});
//Draw ALL elements.
function Draw() {
$.each(elements, (i, element) => {
element.draw(context);
});
}
function Clear() {
context.clearRect(0, 0, $("canvas")[0].width, $("canvas")[0].height);
}
// Start.
$(document).ready(() => {
Draw();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html>
<body>
<canvas></canvas>
</body>
</html>
Hope it helps you out!
The script below draws an image on the left side of the screen and a selection box in the right. It then attempts to redefine the image drawn on the left on each new selection in the selection box on the right by making the imageID dependent on the selection. However, as you can see below, whatever number you select on the right the image remains the same (1) because whilst it might be redrawn, it is not redefined on selection. What I would like to happen is that on selection in the box on the right the number in the image changes with the selection box such that it always correlates with the selection. In other words, when you click on 2 the image changes to the 2nd image in images. I have found two ways of doing this but they are both flawed:
1: Define img in paint's render function. This works but it makes everything run very slowly and the hover animations on the image stop working as expected.
2: Define img in the makeSelectionInfo function. This also works but the hover animations completely stop working if this is done.
I apologise for the long code but I couldn't condense it any more. For the sake of brevity I have only included images for numbers between 1 & 5. Any help will be appreciated.
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
images=['https://i.stack.imgur.com/KfN4z.jpg',
'https://i.stack.imgur.com/MyQS1.png',
'https://i.stack.imgur.com/3Vlfj.jpg',
'https://i.stack.imgur.com/u3NLH.jpg',
'https://i.stack.imgur.com/XnLwl.png'];
var curvedRect = function(text, x, y, w, h) {
this.text = text;
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.hovered = false;
this.clicked = false;
}
curvedRect.prototype.makeCurvedRect = function() {
var delta=0, theta=0, yRotation=this.y;
if (this.hovered) {
delta = 3;
shadowColor = '#000000';
shadowBlur = 20;
shadowOffsetX = 5;
shadowOffsetY = 5;
theta = -0.01;
} else {
delta = 0;
theta = 0;
shadowColor = '#9F3A9B';
shadowBlur = 0;
shadowOffsetX = 0;
shadowOffsetY = 0;
}
var x = this.x-delta;
var y = yRotation-delta;
var w = this.w+(2*delta);
var h = this.h+(2*delta);
var img=new Image();
img.src=images[this.text];
ctx.rotate(theta);
ctx.beginPath();
ctx.lineWidth='8';
ctx.strokeStyle='white';
ctx.moveTo(x+10, y);
ctx.lineTo(x+w-10, y);
ctx.quadraticCurveTo(x+w, y, x+w, y+10);
ctx.lineTo(x+w, y+h-10);
ctx.quadraticCurveTo(x+w, y+h, x+w-10, y+h);
ctx.lineTo(x+10, y+h);
ctx.quadraticCurveTo(x, y+h, x, y+h-10);
ctx.lineTo(x, y+10);
ctx.quadraticCurveTo(x, y, x+10, y);
ctx.shadowColor = shadowColor;
ctx.shadowBlur = shadowBlur;
ctx.shadowOffsetX = shadowOffsetX;
ctx.shadowOffsetY = shadowOffsetY;
ctx.stroke();
ctx.shadowBlur = 0;
ctx.shadowOffsetX = 0;
ctx.shadowOffsetY = 0;
ctx.drawImage(img, x+2.5, y+2.5, w-5, h-5);
ctx.rotate(-theta);
}
curvedRect.prototype.hitTest = function(x, y) {
return (x >= this.x) && (x <= (this.w+this.x)) && (y >= this.y) && (y <= (this.h+this.y));
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
this.hovered = false;
this.clicked = false;
this.lastClicked = false;
}
function makeTextForSelected(text, y) {
ctx.font='bold 12px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='center';
ctx.fillText(text, 200, y);
}
function makeSelectionInfo(text) {
makeTextForSelected(text, 375);
}
selectionForMenu.prototype.makeSelection = function() {
var fillColor='#A84FA5';
if (this.hovered) {
if (this.clicked) {
if (this.lastClicked) {
fillColor='#E4C7E2';
} else {
fillColor='#D5A9D3';
}
} else if (this.lastClicked) {
fillColor='#D3A4D0';
makeSelectionInfo(this.text);
} else {
fillColor='#BA74B7';
}
} else if (this.lastClicked) {
fillColor='#C78DC5';
makeSelectionInfo(this.text);
} else {
fillColor='#A84FA5';
}
ctx.beginPath();
ctx.fillStyle=fillColor;
ctx.fillRect(400, this.y, 350, 30)
ctx.stroke();
ctx.font='10px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, 410, this.y+19);
}
selectionForMenu.prototype.hitTest = function(x, y) {
return (x >= 400) && (x <= (750)) && (y >= this.y) && (y <= (this.y+30)) && !((x >= 400) && (y > 450));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeSelection();
}
catch(err) {}
try {
this.shapes[i].makeCurvedRect();
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='white';
ctx.fillRect(0, 0, 750, 25);
ctx.stroke();
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeBox();
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='#BC77BA';
ctx.fillRect(0, 450, 750, 50);
ctx.stroke();
ctx.font='bold 10px Noto Sans';
ctx.fillStyle='#9F3A9B';
ctx.textAlign='center';
ctx.fillText('Phrase Practice', 365, 17);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeInteractiveButton();
}
catch(err) {}
}
}
Paint.prototype.setHovered = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].hovered = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setClicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setUnclicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
if (shape.constructor.name==this.shapes[i].constructor.name) {
this.shapes[i].clicked = false;
if (shape instanceof selectionForMenu) {
this.shapes[i].lastClicked = this.shapes[i] == shape;
}
}
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
imageID = 0;
var paint = new Paint(c);
var img = new curvedRect(imageID, 112.5, 100, 175, 175);
var selection = [];
for (i=0; i<=30; i++) {
selection.push(new selectionForMenu(i, i, 25+(i*30)));
}
paint.addShape(img);
for (i=0; i<30; i++) {
paint.addShape(selection[i])
}
paint.render();
var clickedShape=0;
var i=0;
function mouseDown(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
if (shape instanceof selectionForMenu) {
imageTextID = shape.id;
if (i==0) {
clickedShape=shape;
i=1;
} else if (i==1) {
i=0;
}
}
paint.setClicked(shape);
}
function mouseUp(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
if (clickedShape instanceof selectionForMenu) {
if (x>400 && y>25 && y<450) {
paint.setUnclicked(shape);
} else if (shape && !(shape instanceof selectionForMenu)) {
paint.setUnclicked(shape);
}
}
}
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setHovered(shape);
}
c.addEventListener('mousedown', mouseDown);
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
</head>
<body>
<canvas id="game" width = "750" height = "500"></canvas>
</body>
</html>
Fixed your code here https://jsfiddle.net/0wq0hked/2/
You can diff to see what I changed, but basically you weren't initializing and adding the multiple curvedRect to the Paint.shapes array. I also added the images as an attribute of curvedRect.
I also had to add a visible parameter to your shapes, as your mouse hover Paint.select function was not functioning properly. The way yours works, shapes that share the same (x,y) do not allow other shapes from being hovered even when they are not visible. Thus multiple shapes occupying the image area to the left stopped the hover from working properly. I suppose you could keep your Paint.select and instance/remove shapes when they are to be drawn, but you do not have shape removal functionality as far as I can tell.
Also, you call render on every event, this is a bad idea. Take a look at requestAnimationFrame and try drawing at the screen refresh rate rather than on user input.
I'm having a problem whit my code.
I draw some circles in a circular path and I expect when to click on them to return something other than 0 in firebug console but that's not happening;
I don't know what is wrong with my code and i hope someone will tell me.
Here's my code:
var canvas, ctx;
var circle_data = [];
function circles(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
circle_data.push(this);
}
circles.prototype = {
draw: function (context) {
context.beginPath();
context.arc(this.x, this.y, this.radius / 5, 0, 2 * Math.PI, false);
context.fillStyle = "red";
context.fill();
}
}
function draw() {
ctx.translate(250, 250);
for (var n = 0; n < 10; n++) {
var radi = (Math.PI / 180);
var x = Math.sin(radi * n * 36) * 70;
var y = Math.cos(radi * n * 36) * 70;
var radius = 50;
var thiscircle = new circles(x, y, radius);
thiscircle.draw(ctx);
}
}
function mouseDown(e) {
var img_data = ctx.getImageData(e.pageX, e.pageY, 1, 1);
console.log(img_data.data[3]);
}
function init() {
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
canvas.addEventListener('mousedown', mouseDown, false);
}
init();
It dosen't matter is i use data[3];
I tried whit console.log(img_data.data[0]+" "+img_data.data[1]+" "+img_data.data[2]);
Still getting 0 0 0
Your detecting the mouse position relative to the page and not the canvas, you need to get the position of the canvas on the page and subtract that from the X and Y of the mouse to find you position relative to the canvas. I use functions similar to the ones below when working with canvas.
getOffsetPosition = function(obj){
/*obj is the Canvas element*/
var offsetX = offsetY = 0;
if (obj.offsetParent) {
do {
offsetX += obj.offsetLeft;
offsetY += obj.offsetTop;
}while(obj = obj.offsetParent);
}
return [offsetX,offsetY];
}
getMouse = function(e,canvasElement){
OFFSET = getOffsetPosition(canvasElement);
mouse_x = (e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft)) - OFFSET[0];
mouse_y = (e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop)) - OFFSET[1];
return [mouse_x,mouse_y];
}
The following code works.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
</head>
<body>
<canvas id="canvas" width="500" height="500" style="background-color:#999999;"></canvas>
</body>
<script>
var canvas,ctx;
var circle_data = [];
function circles(x,y,radius)
{
this.x = x;
this.y = y;
this.radius = radius;
circle_data.push(this);
}
circles.prototype = {
draw: function(context){
context.beginPath();
context.arc(this.x, this.y, this.radius / 5, 0, 2* Math.PI, false);
context.fillStyle = "red";
context.fill();
}
}
getOffsetPosition = function(obj){
/*obj is the Canvas element*/
var offsetX = offsetY = 0;
if (obj.offsetParent) {
do {
offsetX += obj.offsetLeft;
offsetY += obj.offsetTop;
}while(obj = obj.offsetParent);
}
return [offsetX,offsetY];
}
getMouse = function(e,canvasElement){
OFFSET = getOffsetPosition(canvasElement);
mouse_x = (e.pageX || (e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft)) - OFFSET[0];
mouse_y = (e.pageY || (e.clientY + document.body.scrollTop + document.documentElement.scrollTop)) - OFFSET[1];
return [mouse_x,mouse_y];
}
function draw(){
ctx.translate(250, 250);
for (var n = 0; n < 10; n++) {
var radi = (Math.PI/180);
var x = Math.sin(radi*n*36)*70;
var y = Math.cos(radi*n*36)*70;
var radius = 50;
var thiscircle = new circles(x,y,radius);
thiscircle.draw(ctx);
}
}
function mouseDown(e)
{
var pos = getMouse(e,ctx.canvas);
var img_data = ctx.getImageData(pos[0],pos[1],1,1);
console.log(img_data.data[3]);
}
function init() {
canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d');
draw();
canvas.addEventListener('mousedown', mouseDown, false);
}
init();
</script>
</html>