Craftyjs canvas rotation - javascript

If I change the e1 attribute y to 1 or some other positive value this code works, but if the y is 0 or negative it fails. There are no errors but the shape does not appear. If I draw other kind of shapes, same kind of problem occurs. Anyway, rotation values such as 0, 90 and 271 work with y: 0. There is not such a problem with the x value. Why is that? Is it a bug related to Crafty.js?
<script>
Crafty.init(480,680, document.getElementById('test'));
Crafty.c("myComponent", {
init: function () {
this.requires("2D, Canvas");
this.bind("Draw", this._draw_me);
this.ready = true;
},
_draw_me: function (e) {
var ctx = e.ctx;
ctx.beginPath();
ctx.moveTo(e.pos._x, e.pos._y);
ctx.lineTo(e.pos._x + e.pos._w, e.pos._y);
ctx.lineTo(e.pos._x + e.pos._w/2, e.pos._y + e.pos._h);
ctx.lineTo(e.pos._x, e.pos._y);
ctx.fillStyle = "blue";
ctx.fill();
}
});
var e1 = Crafty.e("myComponent")
.attr({x: 100, y: 0, w: 60, h: 60, rotation:180})
.origin("center");
</script>

Set w and h before setting origin. Set the rotation origin before setting the rotation.
This should be clearly documented in the API documentation, I went ahead and opened an issue for that on Crafty's issue tracker.
If you set the origin after the rotation, things get messed up somehow, and the _draw_me function never gets called, as Crafty thinks the triangle sits outside the viewport (camera) and does not need to be drawn. (Observe what happens when you set Crafty.viewport.y = 100 - the triangle appears)
The following snippet works for me. The code sets w and h, origin & rotation, in that order.
Crafty.init();
Crafty.c("myComponent", {
init: function () {
this.requires("2D, Canvas");
this.bind("Draw", this._draw_me);
this.ready = true;
},
_draw_me: function (e) {
var ctx = e.ctx;
ctx.beginPath();
ctx.moveTo(e.pos._x, e.pos._y);
ctx.lineTo(e.pos._x + e.pos._w, e.pos._y);
ctx.lineTo(e.pos._x + e.pos._w/2, e.pos._y + e.pos._h);
ctx.lineTo(e.pos._x, e.pos._y);
ctx.fillStyle = "blue";
ctx.fill();
}
});
var e1 = Crafty.e("myComponent")
.attr({w: 60, h: 60})
.origin("center")
.attr({x: 100, y: 0, rotation: 180});
<script src="https://github.com/craftyjs/Crafty/releases/download/0.7.1/crafty-min.js"></script>

Related

how to assign each block an Id to differentiate between blocks inside a canvas and fetch data according to that id

Hello Fellow Developers,
I'm new to react and canvas api, I want to create 10k blocks(square) of particular dimensions and each block is different from one another by ID. I have created 10k blocks
of 100 * 100 rows and columns and each box is 10 * 10.
Now what I want is to assign an ID to each box to identify that particular box that I select onClick and read data from smart contract
Once the ID is selected I want to able to select multiple boxes using input fields, eg, I selected box# 4 and in input fields I have to select width and height, so when I select width 5 block numbers 5 6 7 8 9 in x-direction will also get selected, similarly if I select height 2 then including 4 one more box is selected in y-direction.
Now these selection can only happen if that box is available to select if its value is false, no-one can select that box no matter the height and width.
I don't know how to assign an ID please anyone can help me in this, It'll be great help
Reference Website to understand my points: https://milliondollartokenpage.com/
Code is attached that I've done so far:
https://jsfiddle.net/ahmedzafar/d3twnfe1/1/
Thanks in advance:)
Canvas has no concept of "ID" for the elements that are drawn, you have to keep track of that...
But canvas as good options to help us deal with this obstacle, my favorite is Path2d:
https://developer.mozilla.org/en-US/docs/Web/API/Path2D/Path2D
Here is a starting point:
class Shape {
constructor(x, y) {
this.path = new Path2D();
this.path.arc(x, y, 16, 0, 2 * Math.PI);
}
draw(ctx, pos) {
ctx.beginPath();
ctx.fillStyle = ctx.isPointInPath(this.path, pos.x, pos.y)? "red": "tan"
ctx.fill(this.path);
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return { x: evt.clientX - rect.left, y: evt.clientY - rect.top };
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
shapes = []
for (var i = 0; i < 6; i++) {
shapes.push(new Shape(50 + i * 40, 40))
}
canvas.addEventListener('mousemove', function(evt) {
ctx.clearRect(0, 0, canvas.width, canvas.height);
shapes.forEach(s => { s.draw(ctx, getMousePos(canvas, evt)) })
}, false);
shapes.forEach(s => { s.draw(ctx, { x: 0, y: 0}) })
<canvas id="canvas" width=500 height=150></canvas>
On that example we can see I used Path2D and the isPointInPath to determine if the mouse is on an element, and we change the fill color accordingly.
Here is another example, this time we detect if user clicked in one of our shapes, and just output some info to the console.
class Shape {
constructor(name, x, y) {
this.obj = {name, x, y}
this.path = new Path2D();
this.path.rect(x, y, 50, 50);
}
draw(ctx) {
ctx.beginPath();
ctx.stroke(this.path);
ctx.strokeText(this.obj.name, this.obj.x +10, this.obj.y +20)
}
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return { x: evt.clientX - rect.left, y: evt.clientY - rect.top };
}
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
shapes = []
shapes.push(new Shape("A", 10, 10))
shapes.push(new Shape("B", 80, 10))
shapes.push(new Shape("C", 150, 10))
canvas.addEventListener('mouseup', function(evt) {
var pos = getMousePos(canvas, evt);
shapes.forEach(s => {
if (ctx.isPointInPath(s.path, pos.x, pos.y))
console.log(s.obj)
})
}, false);
shapes.forEach(s => { s.draw(ctx) })
<canvas id="canvas" width=300 height=100></canvas>
My examples are not React/NodeJs but...
I hope you can easily integrate this concept in your project.
I had a bit of time to play with your sample, I made some changes here two versions:
https://jsfiddle.net/heldersepu/078ztk5h/28/
https://jsfiddle.net/heldersepu/078ztk5h/47/

JavaScript moving objects from starting location to ending locations

I am taking a course online and I can't find help if am stuck..... I am using brackets and p5.js
these are the instructions i have:
Edit the spotlight object by creating x and y properties initialised to your location. Also endX and endY properties initialised to one of the Minsky's location.
Assign the other 2 spotlights and create the required properties.
Make the spotlight move perfectly from you towards the Minskys by adjusting the increments of x and y properties.
If you get everything correct then it will stop over the target.
Adjust x and y properties using
"+=" or "+"
"-=" or "-"
*/
(the minsky brothers are the targets i need the spotlight to be on, the "your location" is the start location)
i will copy and paste my code and the message i get when i submit :
// other variables, you don't need to change these
var img, spotlight_image;
var spotlight1;
var spotlight2;
var spotlight3;
function preload()
{
img = loadImage('scene.png');
spotlight_image = loadImage('spotlight.png')
}
function setup()
{
createCanvas(img.width, img.height);
//complete the initialisation of the first spotlight
//with properties x, y, endX and endY
spotlight1 = {
image: spotlight_image
x: 164,
y: 810,
endX: 780,
endY: 640,
}
//Initialize the second and third spotlights
spotlight2 = {
image: spotlight_image
x: 164,
y: 810,
endX: 480,
endY: 474,
}
spotlight3 = {
image: spotlight_image
x: 164,
y: 810,
endX:766,
endY: 290,
}
}
function draw()
{
image(img, 0, 0);
// alter the properties x and y of the objects below to animate the spotlights
spotlight.x += 1;
spotlight.y += 1;
////////// DO NOT CHANGE ANYTHING BELOW /////////////
var spotlights = [spotlight1, spotlight2, spotlight3];
var spotlightSize = 300;
blendMode(BLEND);
background(30);
for (var i = 0; i < spotlights.length; i++)
{
var spotlight = spotlights[i];
//stop the spotlight if it's near enough to endx and endy
if(spotlight)
{
//stop the spotlight if it goes off of the screen
spotlight.x = min(spotlight.x, 960);
spotlight.y = min(spotlight.y, 945);
spotlight.x = max(spotlight.x, 0);
spotlight.y = max(spotlight.y, 0);
if (abs(spotlight.endX - spotlight.x) < 50
&& abs(spotlight.endY - spotlight.y) < 50)
{
spotlight.x = spotlight.endX;
spotlight.y = spotlight.endY;
}
image(spotlight.image, spotlight.x-spotlightSize/2,
spotlight.y-spotlightSize/2, spotlightSize, spotlightSize);
}
}
blendMode(DARKEST);
image(img, 0, 0);
////////// DONOT CHANGE ANYTHING ABOVE /////////////
}
the message i get when submitting:
Error in compile
SyntaxError: Unexpected identifier
Blockquote
This is fairly easy to do. If I understand correctly, you want to move an object towards a point over a certain amount of time. All you have to do is get the range between the two X coordinates and the two Y coordinates, divide them by how many frames you want it to be moving for, and update its position by that amount every frame until it reaches its destination.

Using easelJS to free transform shapes on pressmove

I want to replicate the basic functionality of a free transform tool (no rotation), by dragging on the border of a easeljs Shape and adjusting the container to match it. I'm currently using the scaleX and scaleY properties and it sort of works but is not quite right.
If you do one scaling transformation it works pretty well. However if you release, then do another scaling transformation, it jumps very glitchily, and can occasionally break sending the x/y coordinates all the way to stage 0. Any help on this issue would be great!
http://jsfiddle.net/frozensoviet/dsczvrpw/13/
//circle
var circle = new createjs.Shape(new createjs.Graphics()
.beginFill("#b2ffb2")
.drawCircle(0, 0, 50));
circle.setBounds(0, 0, 50, 50);
//create the border as a seperate object
var cBorder = new createjs.Shape(new createjs.Graphics().setStrokeStyle(10)
.beginStroke("#000").drawCircle(0, 0, 50));
cBorder.setBounds(0, 0, 50, 50);
//add both to the container
circleContainer.addChild(circle);
circleContainer.addChild(cBorder);
var cWidth = circleContainer.getBounds().width;
var cHeight = circleContainer.getBounds().height;
//find initial mouse position relative to circle center
cBorder.on("mousedown", function (evt) {
//initial mouse pos
this.initial = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
});
//set the relevant circle axis scale to ratio of mouse pos/initial mouse pos
cBorder.on("pressmove", function (evt) {
//current moouse pos
this.offset = {
x: Math.abs(-(circleContainer.x - evt.stageX)),
y: Math.abs(circleContainer.y - evt.stageY)
};
if (this.initial.x > this.initial.y) {
//sides
circleContainer.scaleX = this.offset.x / this.initial.x;
} else if (this.initial.x < this.initial.y) {
//top/bottom
circleContainer.scaleY = this.offset.y / this.initial.y;
} else {
//diagonals
circleContainer.scaleX = this.offset.x / this.initial.x;
circleContainer.scaleY = this.offset.y / this.initial.y;
}
stage.update();
});
The issue is your initial calculations don't account for the change in the scale of the circle. You would have to transform the coordinates using localToGlobal. Fortunately, there is an even easier way:
this.initial = {
x: Math.abs(evt.localX),
y: Math.abs(evt.localY)
};
You can also turn on ignoreScale on the border, which makes it not stretch:
createjs.Graphics().setStrokeStyle(10,null,null,null,true) // The 5th argument
Lastly, your bounds setting might work for your demo, but it is not correct. Your circle draws from the center, so it should be:
cBorder.setBounds(-25, -25, 50, 50);
Here is an updated fiddle: http://jsfiddle.net/tfy1sjnj/3/

increase performance on html canvas mousemove image mask

I have a canvas that is drawing an image and clipping to create the effect that the image is being revealed. I have the code working properly I have tried using a debouce method and also rAF to increase the canvas rendering performance but I only saw small gains if any.
I suspect the way I am iterating through my array of x and y coordinates could be the issue.
It seems to lag quite a bit when it is out putting the array in console about the same rate as the circle appear on the screen.
Here is the redraw function:
function redraw(mouse) {
m.push(mouse);
m.forEach(function (a) {
ctx.drawImage(img, 0, 0);
ctx.beginPath();
ctx.rect(0, 0, 500, 500);
ctx.arc(a.x, a.y, 70, 0, Math.PI * 2, true);
ctx.clip();
ctx.fillRect(0, 0, 500, 500)
})
}
I guess what I am looking for is some advice to speed up my code so the rendering of the circles seems more like drawing.
Here is the working demo -> http://jsfiddle.net/naeluh/4h7GR/
There are several issues here :
• Your mouse code is a nightmare, traversing the DOM on every move.
• You are redrawing everything on each move.
So i suggest a way more efficient solution :
• stack two canvases, the one below is your image, the one on top is the mask.
• Deal efficiently with the mouse.
• Only clear part of the mask canvas on mouse move : just one circle drawn on the mask canvas for each move.
(for that i used a globalCompositeOperation = 'destination-out' )
Result is perfectly smooth either on Firefox, Chrome, or Safari .
(tested on mac OS).
the fiddle :
(you have to click to clear)
http://jsfiddle.net/gamealchemist/4h7GR/22/
html
<canvas style='position: absolute; top: 0;left: 0;' id="canvas1" width="500" height="500"></canvas>
<canvas style='position: absolute;top: 0;left: 0;' id="canvas2" width="500" height="500"></canvas>
js
var can = document.getElementById("canvas1");
var ctx = can.getContext("2d");
var can2 = document.getElementById("canvas2");
var ctx2 = can2.getContext("2d");
var img = new Image();
img.onload = function () { ctx.drawImage(img,0,0); };
img.src = "http://placekitten.com/500/500";
ctx2.fillStyle='#000';
ctx2.fillRect(0,0,500,500);
ctx2.globalCompositeOperation = 'destination-out';
function clearThis(x,y) {
console.log('toto');
ctx2.fillStyle='#F00000';
ctx2.beginPath();
ctx2.arc(x, y, 70, 0, Math.PI * 2, true);
ctx2.fill();
}
var mouse = {
x: 0,
y: 0,
down: false
};
function setupMouse(canvas, onMouseMove, preventDefault) {
var rectLeft, rectTop;
var hook = canvas.addEventListener.bind(canvas);
var mouseDown = updateMouseStatus.bind(null, true);
var mouseUp = updateMouseStatus.bind(null, false);
hook('mousedown', mouseDown);
hook('mouseup', mouseUp);
hook('mousemove', updateCoordinates);
hook('scroll', updateRect);
// var mouseOut = function() { mouse.down=false ; } ;
// hook('mouseout', mouseOut);
function updateMouseStatus(b, e) {
mouse.down = b;
updateCoordinates(e);
if (preventDefault) {
e.stopPropagation();
e.preventDefault();
}
}
function updateCoordinates(e) {
mouse.x = (e.clientX - rectLeft);
mouse.y = (e.clientY - rectTop);
onMouseMove(mouse.x, mouse.y);
}
function updateRect() {
var rect = canvas.getBoundingClientRect();
rectLeft = rect.left;
rectTop = rect.top;
}
updateRect();
};
setupMouse(can2, clearThis, true);
The Above Code will do Fine .. But nEed some Editing
I have Edited the Code in Fiddle ..and i beleive there Is some Improvement in perforamnce
So I looked a little more and found a bug as expected.
The main problem is the accumulation of the drawing path.
Why Need to add clip and fillRect at every go ..Do it at last... the Major issue solved,Like
can.addEventListener("mousemove", function (e) {
var mouse = getMouse(e, can);
requestAnimationFrame(function () {
redraw(mouse);
ctx.clip();
ctx.fillRect(0, 0, 500, 500);
console.log(mouse);
});
}, false);
2.The Updated JSFiidle is
UpdatedFiddle

Moving All Images In A Clipped Canvas

Ok so I have just looked into the Canvas element but I have hit a snag.
I found a demo that moves three images over the top of one another using arrow keys.
However, I clipped one of them and the underlying image does not move now even though the above clipped image does.
HTML:
<canvas id="parallax-canvas">
Sorry, your browser does not support HTML5 Canvas.
</canvas>
CSS:
.parallax-canvas {
width: 400px;
height: 300px;
}
JavaScript:
$(document).ready(function() {
var w = $("#parallax-canvas").width();
var h = $("#parallax-canvas").height();
var sky = new Image();
sky.src = "assets/img/sky.jpg";
var skydx = 2;
var skyx = 0;
var mountains = new Image();
mountains.src ="assets/img/mountains.png";
var mountainsdx = 10;
var mountainsx = 0;
var jeep = new Image();
jeep.src ="assets/img/jeep.png";
var jeepx = 100;
var jeepy = 210;
var jeepsx = 0;
var jeepsxWidth = 155;
var cntx = $("#parallax-canvas")[0].getContext("2d");
setInterval(draw, 10, cntx);
$(window).keydown(function(evt) {
switch (evt.keyCode) {
case 37: // Left arrow
if ((skyx + skydx) > skydx)
skyx -= skydx;
else
skyx = w;
if ((mountainsx + mountainsdx) > mountainsdx)
mountainsx -= mountainsdx;
else
mountainsx = 398;
if (jeepsx > 0)
jeepsx -= jeepsxWidth;
else
jeepsx = (jeepsxWidth*2);
break;
case 39: // Right arrow
if ((skyx + skydx) < (w - skydx))
skyx += skydx;
else
skyx = 0;
if ((mountainsx + mountainsdx) < (w - mountainsdx))
mountainsx += mountainsdx;
else
mountainsx = 0;
if (jeepsx < (jeepsxWidth*2))
jeepsx += jeepsxWidth;
else
jeepsx = 0;
break;
}
});
function draw(_cntx) {
drawRectangle(_cntx, 0, 0, w, h);
_cntx.drawImage(sky, skyx, 0, 300, 300, 0, 0, w, 300);
_cntx.beginPath();
_cntx.moveTo(0,300);
_cntx.lineTo(150,150);
_cntx.lineTo(300, 300);
_cntx.closePath();
_cntx.clip();
_cntx.drawImage(mountains, mountainsx, 0, 300, 300, 0, 0, w, 300);
_cntx.drawImage(jeep, jeepsx, 0, jeepsxWidth, 60, jeepx, jeepy, 155, 60);
}
function drawRectangle(_cntx, x, y, w, h) {
_cntx.beginPath();
_cntx.rect(x,y,w,h);
_cntx.closePath();
_cntx.fill();
_cntx.stroke();
}
});
The part where I added the clipping is this section:
function draw(_cntx) {
drawRectangle(_cntx, 0, 0, w, h);
_cntx.drawImage(sky, skyx, 0, 300, 300, 0, 0, w, 300);
_cntx.beginPath();
_cntx.moveTo(0,300);
_cntx.lineTo(150,150);
_cntx.lineTo(300, 300);
_cntx.closePath();
_cntx.clip();
_cntx.drawImage(mountains, mountainsx, 0, 300, 300, 0, 0, w, 300);
_cntx.drawImage(jeep, jeepsx, 0, jeepsxWidth, 60, jeepx, jeepy, 155, 60);
}
If you remove from and including the beginPath method to the clip method it will allow the three images to all move.
WHAT IS HAPPENING: The sky image does not move when pressing left and right arrow keys, the mountain and jeep are clipped but do move within the clipped region.
WHAT I WANT TO HAPPEN: The sky image to move as well as the mountain and jeep in the clipped region.
ALSO:
I would like to know how to move the clipped region with the arrow presses. What I mean is, at the moment there is a clipped region where by pressing left and right the images move but the clipped (cut out) region remains still. I would like to know (if possible) how to go about moving the clipped region with the arrow keys.
Demo being used: http://www.ibm.com/developerworks/web/library/wa-parallaxprocessing/
Unfortunately cannot setup a Fiddle as the images are not on the site so cannot be given a URL but the .zip is on the site and by copying and pasting the bottom part of code where their method is in the JavaScript file that is what I am getting.
Thanks for any help!
EDIT: Thanks Ken for your help with shortening code and efficiency. Nothing so far though has answered my initial questions of how to move the images and also the clipping positions.
You need to set a size for your canvas - don't use CSS to set the size of a canvas:
<canvas id="parallax-canvas" width=500 height=300>
Sorry, your browser does not support HTML5 Canvas.
</canvas>
Likewise you need to read the proper size from the canvas:
$(document).ready(function() {
var canvas = $("#parallax-canvas")[0];
var w = canvas.width;
var h = canvas.height;
...
The for each time you hit the cursor keys you need to redraw everything. The canvas doesn't know what is drawn into it - it's just a bunch of pixels so we need to provide the logic for updates ourselves.
Update seem as I missed you're doing the redraw from a setInterval loop so this doesn not apply so much, but I let the example stay as it will probably be a better approach as you only need to update when something is actually changing. A loop that redraws everything all the time will quickly drain batteries for example..
For example:
$(window).keydown(function(evt) {
switch (evt.keyCode) {
case 37: // Left arrow
if ((skyx + skydx) > skydx)
skyx -= skydx;
else
skyx = w;
if ((mountainsx + mountainsdx) > mountainsdx)
mountainsx -= mountainsdx;
else
mountainsx = 398;
if (jeepsx > 0)
jeepsx -= jeepsxWidth;
else
jeepsx = (jeepsxWidth*2);
draw(cntx );
break;
...
This you need to repeat for the other moves as well.
You will also run into problems with the way you are loading the images as loading is asynchronous. You need to handle loading by tapping into the onload handler:
var sky = new Image();
sky.onload = functionToHandleLoad;
sky.src = "assets/img/sky.jpg";
As you are loading many images you would need to count the images or use a bulk loader such as my YAIL loader.
For clipping it's important to use save/restore as currently browsers doesn't handle manual reset of clip regions that well:
function draw(_cntx) {
drawRectangle(_cntx, 0, 0, w, h);
_cntx.drawImage(sky, skyx, 0, 300, 300, 0, 0, w, 300);
_cntx.beginPath();
_cntx.moveTo(0,300);
_cntx.lineTo(150,150);
_cntx.lineTo(300, 300);
_cntx.closePath();
_cntx.save(); /// save current clip region
_cntx.clip();
_cntx.drawImage(mountains, mountainsx, 0, 300, 300, 0, 0, w, 300);
_cntx.drawImage(jeep, jeepsx, 0, jeepsxWidth, 60, jeepx, jeepy, 155, 60);
_cntx.restore(); /// reset clip
}
At the time of answering there where no fiddle available so I haven't checked the other parts of the code, but this should be a good start I think.
This function
function drawRectangle(_cntx, x, y, w, h) {
_cntx.beginPath();
_cntx.rect(x,y,w,h);
_cntx.closePath();
_cntx.fill();
_cntx.stroke();
}
can be simplified to:
function drawRectangle(_cntx, x, y, w, h) {
_cntx.fillRect(x, y, w, h);
_cntx.strokeRect(x, y, w, h);
}
No need to close path on a rect and beginPath is not needed with fillRect/strokeRect.

Categories