can't add new objects on canvas (fabric.js) - javascript

I am working on a project using fabric.js and facing this problem.
i have a function fun() and its called on event onclick, so basically i am expecting a circle everytime i click on my screen but as result all i get is a blank screen. alerting the objetcs sure gives me the number of objects with the type circle and is incremented everytime i click on the screen but i cant see the circles on the screen, its like the cirlces are there but not visible.
here is the code:
<html>
<head>
<script type="text/javascript" src="C:\Users\Sunny\Documents\6th Sem Ka Maal\Graph\project\GUI\fabric.js-1.5.0\dist\fabric.js"></script>
<script>
var canvas = new fabric.Canvas('p');
function fun(event){
var x = event.clientX;
var y = event.clientY;
alert(x);
alert(y);
alert(canvas);
var circle= new fabric.Circle({
radius: 50, fill: 'green', left: x, top: y
});
alert(circle);
canvas.add(circle);
}
</script>
</head>
<body>
<canvas id="p" height="993" width="1920" onclick="fun(event)"></canvas>
</body>
</html>
why am i not able to see the circles?

Try wiring up the event in Javascript instead, using Fabric's on() listener.
canvas.on('mouse:up', fun);
Read more about fabric events here:
https://github.com/kangax/fabric.js/wiki/Working-with-events
Last year I was messing around with Fabric for an OU project for point-to-point drawing, you can have a look at it here might give you some ideas.

Related

Snap.svg setStops causing error

Below is an interactive example attempting to implement setStops to a radial gradient. When the 'setStops' button is clicked an error message occurs: "setStops is not a function"
Am I using this correctly?
<!DOCTYPE HTML>
<html>
<head>
<title>Snap.svg setStops</title>
<script type="text/javascript" src="http://svgDiscovery.com/_SNP/snap.svg-min.js"></script>
</head>
<body>
<svg id=mySVG width=400 height=200></svg>
<br><button onClick=stopsSet()>setStops</button>
<script>
var SNPsvg = Snap("#mySVG");
var radialGradient = SNPsvg.gradient("r(.5,.5,.5,.5)#000-#f00-#fff-green");
var circle = SNPsvg.circle(200,100,50).attr({fill: radialGradient});
//---button---
function stopsSet()
{
radialGradient.setStops("#fff-#000-#f00-#fc0");
circle.attr({fill: radialGradient});
}
</script>
</body>
</html>
Seems to be a bug in snap.svg. The code for linearGradient looks like this:
function gradientLinear(defs, x1, y1, x2, y2) {
var el = Snap._.make("linearGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
el.setStops = GsetStops;
but for radialGradients it's this:
function gradientRadial(defs, cx, cy, r, fx, fy) {
var el = Snap._.make("radialGradient", defs);
el.stops = Gstops;
el.addStop = GaddStop;
el.getBBox = GgetBBox;
if (cx != null) {
The code to add the setStops function is missing.
I've created a pull request in Snap.svg to fix this so hopefully one of the maintainers will merge it and include it in a subsequent release. In the meantime you could always make the same change to a local copy of Snap.
I think that's because setStops is only available for a linearGradient and not a radialGradient (not sure if that's by design or not).
If you try gradient("L(0, 0, 100, 100)#000-#f00:25-#fff") I think the error will go away. Naturally that's probably not what you want, but I'm just explaining why I think the error is there.
One thing you can always do with Snap if you get stuck, is use a bit of your own markup if it's not supported direct, and add it in. Eg Snap.parse('Some SVG Markup')
var svgMarkup = Snap.parse('<defs><radialGradient id="exampleGradient"><stop offset="10%" stop-color="gold"/><stop offset="95%" stop-color="green"/></radialGradient></defs>');
SNPsvg.append( svgMarkup );
var radialGradient = SNPsvg.select('#exampleGradient')
var circle = SNPsvg.circle(200,100,50).attr({fill: radialGradient});
Example jsfiddle

Javascript function from a JS file doesn't get called from the canvas

I'm trying to learn about JavaScript and the html5 canvas, however, it's proving a little confusing and I don't understand why it doesn't seem to work...
I am working on creating a simple map that has some of the capabilities of google maps(drag and drop/zoom in/out/etc). In order to do this, I chose html5 canvas and easeljs for the drag and drop functions.
I have a javascript file (path.js) which contains 2 functions:
pathConstructor() - example function from the easeljs tutorial
drawMap() - copy of the first function slightly modified (and probably wrong right now)
Everything worked fine when I called pathConstructor() from the canvas, however, after I replaced it with drawMap(), everything stopped working. It won't even work if I replace drawMap() with pathContrcutor() right now.
I put some alerts before and after calling the function from the canvas and inside pathConstructor(). The before alert goes off but the others don't so for some reason the function never gets called...
If I use the pathConstructor code as inline code in the canvas then it works just fine, however, I would like to avoid that since I believe it's bad programming. I want it to be neat and each script to have its own file.
Anyone know why this is happening?
HTML
<!Doctype html>
<html>
<head>
<script src="http://code.createjs.com/easeljs-0.7.0.min.js"></script>
<script src="path.js" type="text/javascript"></script>
</head>
<body>
<canvas id="canvas" width="1300px" height="800px"style="border:1px dotted black;">
<script>pathConstructor();</script>
</canvas>
</body>
</html>
Javascript
var stage;
function pathConstructor() {
alert('inside pathConstructor');
stage = new createjs.Stage('canvas');
// this lets our drag continue to track the mouse even when it leaves the canvas:
// play with commenting this out to see the difference.
stage.mouseMoveOutside = true;
var circle = new createjs.Shape();
circle.graphics.beginFill("red").drawCircle(0, 0, 50);
var label = new createjs.Text("drag me", "bold 14px Arial", "#FFFFFF");
label.textAlign = "center";
label.y = -7;
var dragger = new createjs.Container();
dragger.x = dragger.y = 100;
dragger.addChild(circle, label);
stage.addChild(dragger);
dragger.on("pressmove", function(evt) {
// currentTarget will be the container that the event listener was added to:
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
// make sure to redraw the stage to show the change:
stage.update();
});
stage.update();
}
function drawMap() {
stage = new createjs.Stage('canvas');
var bitMap = new createjs.Bitmap('middle-earth-map.jpg');
stage.mouseMoveOutside = true;
var dragger = new createjs.Container();
dragger.x = dragger.y = 0;
dragger.addChild(bitMap);
stage.addChild(dragger);
dragger.on('pressmove', function(evt2)) {
evt2.currentTarget.x = evt2.stageX;
evt2.currentTarget.y = evt2.stageY;
stage.update();
});
stage.update();
}
For me it's working fine, you just have to remove that extra ")" in dragger.on('pressmove', function(evt2)) {;
dragger.on('pressmove', function(evt2)) {
evt2.currentTarget.x = evt2.stageX;
evt2.currentTarget.y = evt2.stageY;
stage.update();
});

drag or move image inside a canvas using touchmove in HTML5 for touch devices

I'm creating a HTML5 game which can be made to run on Android also. I went through few articles and didn't get the solution yet. I have a image which i'm generating through javascript and I want to move this image using touchmove so that I can run it in my Android device. This is the code:
gameCanvas.addEventListener("touchmove", touchXY, true);
function touchXY(e) {
if (!e)
var e = event;
e.preventDefault();
avatarX = e.targetTouches[0].pageX - gameCanvas.offsetLeft;
avatarY = e.targetTouches[0].pageY - gameCanvas.offsetTop;
}
This is not working. I got this code from https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HTML-canvas-guide/AddingMouseandTouchControlstoCanvas/AddingMouseandTouchControlstoCanvas.html
And this is my canvas:
<canvas id="gameCanvas" onclick="setUpGame();" width="400" height="300"></canvas>
This is my image:
avatarImage.src = "img/avatar.png";
gameCanvas.getContext("2d").drawImage(avatarImage, Math.random() * 100, Math.random() * 100);
I just want to move the image inside the canvas.
I wrote a full example, hopefully it's not too verbose.
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
</head>
<body>
<canvas id='canvas' width='512' height='512'></canvas>
<script>
var c=document.getElementById('canvas'),
ctx=c.getContext('2d'),
activeBox='none',
//populate the map with objects
box=[
{
x:256,
y:128,
width:32,
height:64
},
{
x:128,
y:64,
width:64,
height:64
},
{
x:32,
y:32,
width:32,
height:32
},
];
function draw(){
//clear the screen, draw population
ctx.clearRect(0,0,c.width,c.height);
for(var i=0;i<box.length;i++){
ctx.fillRect(box[i].x,box[i].y,box[i].width,box[i].height);
}
//repeat at 60fps if possible, pause if window looses focus
requestAnimationFrame(draw);
}
function startTouch(e){
//this makes it easier to write control flow later and keeps XY relative to canvas
var xTouch=e.touches[0].pageX-c.offsetLeft,
yTouch=e.touches[0].pageY-c.offsetTop;
//its best to go through this loop in touchstart, because it only happens once per touch
for(var i=0;i<box.length;i++){
if(xTouch>box[i].x&&xTouch<box[i].x+box[i].width){
if(yTouch>box[i].y&&yTouch<box[i].y+box[i].height){
activeBox=i;
}
}
}
}
function moveTouch(e){
//grab a box by the center
if(activeBox!='none'){
box[activeBox].x=e.changedTouches[0].pageX-box[activeBox].width/2;
box[activeBox].y=e.changedTouches[0].pageY-box[activeBox].height/2;
}
}
function endTouch(){
//clear active so that dragging empty space wont move the last active box
activeBox='none';
}
canvas.addEventListener('touchstart',startTouch);
canvas.addEventListener('touchmove',moveTouch);
canvas.addEventListener('touchend',endTouch);
window.addEventListener('load',draw);
</script>
</body>
</html>
I used fillRect for simplicity, but if you want to replace it with drawImage you'll need to create a new element for each and add a source property to the box object array. Here's a partial example.
//you need a new one of these for every image
var img=new Image();
img.src='http://www.w3schools.com/images/w3logotest2.png';
var box={
source:img,
x:Math.floor((Math.random()*256)+1),
y:Math.floor((Math.random()*256)+1)
};
//make sure the image doesnt load before the script
window.onload=function(){
ctx.drawImage(img,box.x,box.y);
}

Canvas and objects

I'm experimenting with canvas and javascript and was wondering if there was a way to group objects together so they can be treated as a single object within one canvas. So, as a very simple example, say I have a filled circle surrounded by a larger unfilled circle. Now, assume I have 10 of these. I want to move each set separately so is there a way to group each set together as a single object? I don't want to have to make a call to move each object separately.
My example above is a little simple as I actually have 10 or 11 objects grouped together in each cluster. Moving each object in the cluster separately is a pain so I'd like to be able to be able to group them together and make one call to move them.
Any suggestions would be appreciated!
You can use canvas’ “translate” function to draw your circle-group without recalculating!
Location is made simpler for groups when you use a “tranlslate”.
A translate is just the “behind-the-scenes” math that drags your entire group to a new location on the canvas.
You don’t have to do separate calculations for each of your circles. Instead you just do a translate and then draw your circles as if they were in their starting positions.
This is how to impliment translate:
// do a translate to mouseX,mouseY
// all draws will be now be done RELATIVE to mouseX,mouseY
// so if we ctx.translate(100,100)
// then a ctx.rect(0,0,10,10) will actually be drawn at 100,100
ctx.translate(mouseX,mouseY);
// draw the ball group
// notice we didn't have to calculate ANY new positions!!
drawBallGroup();
// translate back after we're done
ctx.translate(-mouseX,-mouseY);
Here’s code and a Fiddle: http://jsfiddle.net/m1erickson/4rJgw/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:10px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
drawBallGroup();
function drawBallGroup(){
drawBall(8,10,5,"red");
drawBall(20,15,10,"green");
drawBall(25,25,8,"blue");
drawBall(5,22,10,"orange");
drawBall(18,30,10,"black");
}
function drawBall(x,y,radius,color){
ctx.beginPath();
ctx.fillStyle=color;
ctx.arc(x,y,radius, 0, 2 * Math.PI, false);
ctx.fill();
}
function handleMouseDown(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
// do a translate to mouseX,mouseY
// all draws will be now be done RELATIVE to mouseX,mouseY
// so if we ctx.translate(100,100)
// then ctx.rect(0,0,10,10) will actually be drawn at 100,100
ctx.translate(mouseX,mouseY);
// draw the ball group
// notice we didn't have to calculate ANY new positions!!
drawBallGroup();
// translate back after we're done
ctx.translate(-mouseX,-mouseY);
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
}); // end $(function(){});
</script>
</head>
<body>
<p>Click to move the ball group to a new location</p>
<p>WITHOUT recalculating each new ball position!</p><br/>
<canvas id="canvas" width=400 height=500></canvas>
</body>
</html>
As pointed out in the comments, there's nothing in the Canvas API which supports grouping of objects. However, there are various libraries for Canvas which you could look into which support this type of functionality. This stackoverflow question might be a good place to start investigating.
easel.js, for example, supports the nesting and grouping of objects. If you're familiar with the ActionScript displayList you'll find it really intuitive as the API is very similar.
Your example of grouping an outer and inner circle and manipulating it as a single object would look something like this with easel:
// Create a stage by getting a reference to the canvas
var stage = new createjs.Stage("canvas");
// Create a container for the circle.
var circle = new createjs.Container();
// Create the outer circle
var outerCircle = new createjs.Shape();
outerCircle.graphics.beginFill("red").drawCircle(0, 0, 40);
// Create the inner circle
var innerCircle = new createjs.Shape();
innerCircle.graphics.beginFill('green').drawCircle(0, 0, 30);
// Add inner and outer circles instance to circle container
circle.addChild(outerCircle);
circle.addChild(innerCircle);
// Now we can move the circle container and its children as a single object
circle.x = circle.y = 40;
// Add circle container instance to stage display list.
stage.addChild(circle);
// Update stage will render next frame
stage.update();
I did something similar recently and I solved this by drawing the group of objects to an off screen (in memory) canvas. Moving this canvas essentially moves the "group".
Depending on what your actual goal is, this may be inflexible - but it's another option to consider.
Well, you could put all your objects in one array and then make a loop, like this:
var objects = [
{x: 200, y: 130, radius: 36, color: "red"}
,
{x: 150, y: 80, radius: 31, color: "blue"}
,
{x: 20, y: 210, radius: 22, color: "green"}
];
for (var i = 0; i < objects.length; i++) {
context.beginPath();
context.arc(objects[i].x, objects[i].y, objects[i].radius, 0, Math.PI * 2, false);
context.fillStyle = objects[i].color;
context.fill();
context.closePath();
}
:D

Javascript Canvas Image onclick not working

I am failing at getting a DOM Image onclick event to work.
var context = document.getElementById('canvas').getContext('2d');
var image = new Image();
image.src = "foo.png"
image.onclick = function(e) { console.log("clicked"); }
setInterval(function() {
context.drawImage(image, 100, 100, 50, 50);
};
Why do I not get the log message when i click on the image. In developer tools i can see the onclick function is not null for the image.
Yes, what Musa said...and a few other things.
Some changes to your code
Image.src=”foo.png” should come after the image.onclick function
Context.drawImage should be inside the image.onclick function
setInterval is not needed as far as I can see
Try this:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var context=document.getElementById("canvas").getContext("2d");
var image=new Image();
image.onload=function(){
context.drawImage(image,0,0);
}
image.src="http://i.imgur.com/nJiIJIJ.png";
document.getElementById("canvas").addEventListener("click", function(){console.log("clicked");}, false);
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
You cannot set onclick for a particular image added in canvas . You can set onclick for the whole canvas alone so you have to use any third party js or else you should do some calculations which finds that you clicked on the image of the canvas ..
Other users are right.
The image you draw on the canvas is a DOM element but it is rendered in a position which is not stored in the DOM.
This doesn't mean you can access it's position and compare it with the mouse position.
I'm using an external library here, but it does what you need: http://jsfiddle.net/Saturnix/cygUH/
this is the library used.
Since I can't post link to jsfiddles without posting the code, here's the script I've wrote for you.
function demo(g) {
g.ctx.font = "bold 16px Arial";
g.draw = function () {
g.ctx.clearRect(0, 0, g.width, g.height)
var posX = 0;
var posY = 0;
g.ctx.drawImage(image, posX, posY);
if (g.mouseX > posX && g.mouseX < image.width &&
g.mouseY > posY && g.mouseY < image.height &&
g.mousePressed)
g.ctx.fillText("You're clicking the image!", g.mouseX, g.mouseY);
}
}
You can cast a ray (with an onclick on the canvas) into the canvas and manually test your images for intersection with the ray. You should write a
objectsUnderPoint( x, y );
function that returns an array of all the images that intersect with the ray at x, y.
This is the way it is usually done in 3D engines as well. You ofcourse need to keep the image position as a property of the image for intersection testing.

Categories