Translate an html5 canvas example to svg - html

I got familiarize with canvas with the help of lot of resources available online, and trying to compare the same with svg. My application needs to draw limited number of shapes, but need to be interactive. I think svg would be more suitable being the shapes are dom elements. it would be great help if someone can translate the canvas example (see demo) to svg with only dependency on jQuery and html5 (don't worry about IE)
In the example, I need to draw a rectangle using mouse (left click and drag). you may add each element to the dom (in canvas I may have to keep an array for the rect object, as the screen clears on each event).
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script type="text/javascript" src="draw.js"></script>
</head>
<body>
<canvas id="cvs" height="600" width="800"></canvas>
</body>
< /html>
$(document).ready(function() {
var cvs = $("#cvs"),
ctx = cvs.get(0).getContext("2d");
var v_bufX, v_bufY, v_bufW, v_bufH;
var box = function ( ctx, style, x, y, w, h ) {
ctx.beginPath();
ctx.rect( x, y, w, h );
ctx.closePath();
if ( style.fill ) {
ctx.fillStyle = style.fill;
ctx.fill();
}
if ( style.stroke ) {
ctx.strokeStyle = style.stroke;
ctx.lineWidth = style.width || 1;
ctx.stroke();
}
},
draw = function (res) {
var style = {fill:'rgba(96,185,206, 0.3)',stroke:'rgb(96,185,206)',width:.5};
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
box(ctx, style, res.x, res.y, res.w, res.h);
};
var rect = {
reset : function () {
this.x0 = this.y0 = this.x = this.y = this.w = this.h = -1;
this.started = this.dragging = false;
},
mousedown : function (e) {
this.reset();
this.started = true;
this.x0 = e._x;
this.y0 = e._y;
},
mousemove : function (e) {
if (!this.started) {
return;
}
var x = Math.min(e._x, this.x0),
y = Math.min(e._y, this.y0),
w = Math.abs(e._x - this.x0),
h = Math.abs(e._y - this.y0);
console.log(x, y, w, h);
if (!w || !h) {
return;
};
this.x = x;
this.y = y;
this.w = w;
this.h = h;
draw(this);
},
mouseup : function (ev) {
if (this.started) {
this.mousemove(ev);
this.started = false;
draw(this);
}
}
};
$(window).mousedown(function(e) {
var canvasOffset = cvs.offset();
e._x = Math.floor(e.pageX-canvasOffset.left);
e._y = Math.floor(e.pageY-canvasOffset.top);
rect.mousedown(e);
});
$(window).mousemove(function(e) {
var canvasOffset = cvs.offset();
e._x = Math.floor(e.pageX-canvasOffset.left);
e._y = Math.floor(e.pageY-canvasOffset.top);
rect.mousemove(e);
});
$(window).mouseup(function(e) {
var canvasOffset = cvs.offset();
e._x = Math.floor(e.pageX-canvasOffset.left);
e._y = Math.floor(e.pageY-canvasOffset.top);
rect.mouseup(e);
});
});

I'm not willing to rewrite an entire example, but here are some resources that might help:
Embedding SVG in XHTML5 - includes a simple JavaScript that creates some of the elements programmtically.
Dragging Transformed Elements - uses my own dragging code and accounts for translations in transformed hierarchies.
SVGPan - a nice library for panning and zooming
Raphael - a library designed to create SVG/VML (for old IE) from JavaScript, including its own draggable implementation.
KevLinDev - a venerable but incredibly-rich source of tutorials and code related to SVG.

Related

HTML Canvas to WPF XAML Canvas

I have an ASP.NET application that allows users to click or tap on a Canvas to indicate pain locations on a body image. A body image is displayed on the Canvas and is the same size as the Canvas.
function drawBodyMap() {
var c = document.getElementById('myCanvas');
var ctx = c.getContext('2d');
var imageObj = new Image();
imageObj.src = 'https://.../body.jpg';
imageObj.onload = function () {
ctx.drawImage(imageObj, 0, 0, 600, 367);
};
}
<canvas id="myCanvas" width="600" height="367"></canvas>
<script>
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
canvas.addEventListener('mouseup', function (evt) {
if (ixPos > 9)
return;
var mousePos = getMousePos(canvas, evt);
bodyX[ixPos] = mousePos.x;
bodyY[ixPos] = mousePos.y;
painType[ixPos] = pain_type;
ixPos++;
ctx.beginPath();
ctx.arc(mousePos.x, mousePos.y, 8, 0, 2 * Math.PI);
if (pain_type == 1)
ctx.fillStyle = "#DC143C";
else if (pain_type == 2)
ctx.fillStyle = "#EA728A";
else if (pain_type == 3)
ctx.fillStyle = "#DAA520";
else if (pain_type == 4)
ctx.fillStyle = "#008000";
else if (pain_type == 5)
ctx.fillStyle = "#4169E1";
ctx.fill();
}, false);
</script>
The X,Y points added to the Canvas on the body image are saved to a database. These points are then loaded into a WPF application that displays the same body image on an XAML Canvas. C# code then adds the points over the image.
WPF CODE:
private void DisplayBodyPain()
{
List<BodyPain> pain = gFunc.sws.GetBodyPain(MemberID);
foreach (BodyPain bp in pain)
{
Border b = new Border();
b.Tag = bp.PainType.ToString();
b.Cursor = Cursors.Hand;
b.Width = 16;
b.Height = 16;
b.CornerRadius = new CornerRadius(8);
b.Background = GetPainBrush((byte)bp.PainType);
cvsBody.Children.Add(b);
Canvas.SetTop(b, bp.YPos);
Canvas.SetLeft(b, bp.XPos);
}
}
The problem I have is that the points drawn on the XAML Canvas are all slightly different from the points that were drawn on the HTML Canvas. Each point is not in exactly the same location.
Is there a way I can fix this? Should I be doing it differently?
HTML Canvas
WPF Canvas
I think you need to subtract the size of the marker from the coordinate where you want to place it. For the last two lines, try this instead:
Canvas.SetTop(b, bp.YPos - (b.Height / 2));
Canvas.SetLeft(b, bp.XPos - (b.Width / 2));
By subtracting half the marker's height and width, the center of the marker is placed on the desired coordinates.

ctx.store() redrawing the previous points also [duplicate]

I have a web application where you can draw a rectangle on a canvas. I use two canvas elements: one for the preview while drawing and another one laying exactly under the other one for drawing it.
The problem I have is that in Internet Explorer, canvas2.width = canvas2.width doesn't clear the content of canvas2, which is necessary because for every mousemove the rectangle gets drawn again. I also tried context2.clearRect(0,0,canvas2.width,canvas2.height), but, however, then the preview rectangle doesn't get drawn at all. Try it out on http://jsfiddle.net/Y389a/2/
HTML:
<canvas id="canvas" width="600" height="400"></canvas>
<canvas id="canvas2" width="600" height="400" onmouseup="return drawLine()" onmousedown="return startLine()"></canvas>
CSS:
#canvas, #canvas2 {
position:absolute;
left:0px;
top:0px;
border-width:1px;
border-style:solid;
border-color:#666666;
cursor:default !important;
}
Javascript:
var x; var xStart;
var y; var yStart;
var clicked = false;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var canvas2 = document.getElementById("canvas2");
var context2 = canvas2.getContext("2d");
context.strokeStyle = "black";
context.lineCap = "round";
canvas2.addEventListener('mousemove', function (evt) {
var rect = canvas2.getBoundingClientRect();
x = evt.clientX - rect.left;
y = evt.clientY - rect.top;
if (clicked) {
canvas2.width = canvas2.width;
context2.rect(xStart, yStart, x - xStart, y - yStart);
context2.stroke();
}
}, false);
function startLine() {
context.beginPath();
xStart = x; yStart = y;
clicked = true;
}
function drawLine() {
clicked = false;
context.rect(xStart, yStart, x - xStart, y - yStart);
context.stroke();
}
Preview
Problem
You are drawing rectangles with context2.rect which is a path command.
Path commands are "remembered" by the canvas until a new context2.beginPath is issued
Therefore, all your previous rects are being remembered and redrawn when you do context2.stroke
Fix
Just put context2.beginPath in your mousemove event handler: http://jsfiddle.net/m1erickson/A8ge6/
canvas2.addEventListener("mousedown",startLine);
canvas2.addEventListener("mouseup",drawLine);
canvas2.addEventListener('mousemove', function (evt) {
var rect = canvas2.getBoundingClientRect();
x = evt.clientX - rect.left;
y = evt.clientY - rect.top;
if (clicked) {
canvas2.width = canvas2.width;
console.log(xStart);
// add beginPath so previous context2.rect's are dismissed
context2.beginPath();
context2.rect(xStart, yStart, x - xStart, y - yStart);
context2.stroke();
}
}, false);
If you only need to stroke a rectangle you can use this version:
context2.strokeRect(xStart, yStart, x - xStart, y - yStart);
instead of rect() + stroke().
This does not add any sub path to the main path but draws directly to canvas. If you need to add other shapes to your path later remember to use beginPath() for rect() in a similar way as you already do in startLine() as rect() add a sub-path.
There is Nothing Wrong with the Code and nothing Wrong With IE 9,What you missed is a l'le concept ,
addEventListener() didn't work For IE instead you have to use attachEvent() for it to make your Code run in IE
//For your code to work in IE
if (!canvas2.addEventListener) {
canvas2.attachEvent("onclick", CanvasFunction);
}
//for rest of the Browser
else {
canvas2.addEventListener("click", CanvasFunction, false);
}
function CanvasFunction(evt)
{
var rect = canvas2.getBoundingClientRect();
x = evt.clientX - rect.left;
y = evt.clientY - rect.top;
if (clicked) {
canvas2.width = canvas2.width;
console.log(xStart);
// add beginPath so previous context2.rect's are dismissed
context2.beginPath();
context2.rect(xStart, yStart, x - xStart, y - yStart);
context2.stroke();
}
}
Playing with Canvas ,remember IE doesn't support addEventListners ..Enjoy Coding

Why does this requestAnmationFrame script not work?

I want to animate a ball using html5 and i implemented this small script . However, I cannot see any animation .. How do I fix this ?
<html>
<head>
<meta charset="utf-8">
<script>
canvas = document.getElementById("canvas");
window.onload=function(){
if (document.createElement("canvas").getContext){
//alert("browser supports canvas");
//console.log(document.getElementById("canvas").getContext);
canvas = document.getElementById("canvas");
shape = new shapes();
shape.drawball(canvas,100,"red");
}
};
function shapes(){
this.drawtriangle = function(canvas){
triangles = new triangle(0,0,0,200,200,200);
triangles.draw(canvas.getContext('2d'));
}
this.drawball = function(canvas,radius,color) {
ball = new Ball(radius,color);
ball.draw(canvas.getContext('2d'),canvas);
}
}
function coordinates(x1,y1){
this.x = x1;
this.y = y1;
}
function angle(angle,speed){
this.angle = angle;
this.speed = speed;
}
function Ball(radius,color){
this.origin = new coordinates(100,100);
this.radius = (radius === "undefined" ) ? 40 : radius;
this.color = (color === "undefined") ? red : color;
this.rotation = 0;
this.index = 0;
this.angles = new angle(0,0.2);
}
Ball.prototype.draw = function(context,canvas){
context.fillStyle = this.color;
context.strokeStyle = "blue";
context.rotate(this.rotation);
context.beginPath();
context.arc(this.origin.x,this.origin.y,this.radius,0,(Math.PI*2),true)
context.closePath();
context.fill();
context.stroke();
this.animate(context,canvas);
}
Ball.prototype.animate = function(context,canvas){
if (this.angles.angle < 1){
context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + 10;
this.origin.y = this.origin.y + 10;
this.angles.angle = this.angles.angle + this.angles.speed;
window.requestAnimationFrame(this.draw(context));
}
}
</script>
<style>
body {
background-color: #bbb;
}
#canvas {
background-color: #fff;
}
</style>
</head>
<body>
<canvas id="canvas" width="1000px" height="1000px">
Your browser dows bot suppoet canvas
</canvas>
</body>
</html>
Your call to requestAnimationFrame() is not passing a callback, it's executing a function and passing its return value which is undefined. I would suggest you change this:
Ball.prototype.animate = function(context,canvas) {
if (this.angles.angle < 1) {
context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + 10;
this.origin.y = this.origin.y + 10;
this.angles.angle = this.angles.angle + this.angles.speed;
window.requestAnimationFrame(this.draw(context));
}
}
to this:
Ball.prototype.animate = function(context,canvas) {
if (this.angles.angle < 1) {
context.clearRect(0,0,1000,1000);
console.log("Animating ... ");
this.origin.x = this.origin.x + 10;
this.origin.y = this.origin.y + 10;
this.angles.angle = this.angles.angle + this.angles.speed;
var self = this;
window.requestAnimationFrame(function() {self.draw(context)});
}
}
so that you pass an appropriate callback function to requestAnimationFrame().
Now that you've included all the code, here is another issue. You can't do this:
canvas = document.getElementById("canvas");
in javascript in the head tag because the DOM is not yet loaded so it will not find that object. You must do that only when the DOM has been loaded either by waiting for an event that signifies the DOM has been loaded or by running the javascript at the every end of the <body> section AFTER all DOM elements.
Then, thirdly, you have to use the browser-specific form of requestAnimationFrame since each browser may have it's own prefix. I used this code:
var reqestAnimationFrame =
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.webkitRequestAnimationFrame;
When I put your script into a jsFiddle and make the above changes, what I find is that the animation runs so quickly that it isn't seen. Your code will need to add a time element to it so that the animation runs over a particular time period. Usually this is done by defining a duration for the animation and at each animation step, you scale the position of the animation based on what percentage of the duration has elapsed.
Here's an example of a time-based animation using requestAnimationFrame: http://jsfiddle.net/jfriend00/nRE7S/

How to identify shapes in Canvas?

If I define a canvas and draw few shapes onto it, then how can I pinpoint each of the shape or image so as to declare events and other properties on the each shape. Consider I have a Rectangle and a triangle. SO can I have some mechanism so as to define them as specific entity and can I deal with them individually. I know about KineticJS but I would like to implement this functionality on my own(For learning purpose). Can anyone pinpoint the way to do it. Or may be an algorithmic approach??
I would like explain pinpoint using mouse events
First of all you have to implement a method to get mouse position
function getMousePos(canvas, evt){
// get canvas position
var obj = canvas;
wrapper = document.getElementById('wrapper');
var top = 0;
var left = 0;
while (obj && obj.tagName != 'BODY') {
top += obj.offsetTop;
left += obj.offsetLeft;
obj = obj.offsetParent;
}
// return relative mouse position
var mouseX = evt.clientX - left + window.pageXOffset+wrapper.scrollLeft;
var mouseY = evt.clientY - top + window.pageYOffset+wrapper.scrollTop;
return {
x: mouseX,
y: mouseY
};
}
Rectangle
Say, we have a rectangle with following values x1, y1, w, h
$(canvas).mousemove(function(e){
//Now call the method getMousePos
var mouseX, mouseY;
var mousePos = getMousePos(canvas, e);
mouseX=mousePos.x;
mouseY=mousePos.y;
// check if move on the rect
if(mouseX>x1 && mouseX<x1+w && mouseY>y1 && mouseY<y1+h)
{
alert('mouse on rect')
}
});
Circle
Say, we have a circle with following values x, y, r
$(canvas).mousemove(function(e){
//Now call the method getMousePos
var mouseX, mouseY;
var mousePos = getMousePos(canvas, e);
mouseX=mousePos.x;
mouseY=mousePos.y;
// check if move on the rect
if(Math.pow(mouseX-x,2)+Math.pow(mouseY-y,2)<Math.pow(r,2))
{
alert('mouse on circle')
}
});
By this way we can pinpoint a object of canvas
You can't use any existing functionality in the DOM for that. So you have to write it yourself. You could start by making an object model like this:
function Shape(x, y) {
var obj = {};
obj.x = x;
obj.y = y;
obj.paint = function(canvasTarget) {
}
return obj;
}
function Rectangle(x, y, width, height) {
var obj = Shape(x, y);
obj.width = width;
obj.height = height;
obj.paint = function(canvasTarget) {
//paint rectangle on the canvas
}
return obj;
}
function Canvas(target) {
var obj = {};
obj.target = target
obj.shapes = [];
obj.paint = function() {
//loop through shapes and call paint
}
obj.addShape(shape) {
this.shapes.push(shape);
}
}
After making the object model you could use it to draw the shapes like this:
var cnv = new Canvas();
cnv.addShape(new Rectangle(10,10,100,100));
cnv.paint();
Then you can handle the onclick event on the canvas and determine which shape is clicked on.

HTML5 pixel manipulation is not working

I'm trying to manipulate the pixels of an image to invert the color and write back to the canvas. But it's not working. Here's the code:
<script type="text/javascript">
<!--
window.addEventListener('load', function () {
var elem = document.getElementById('myCanvas');
var context = elem.getContext('2d');
var img = new Image();
img.addEventListener('load', function () {
var x = 0, y = 0;
context.drawImage(this, x, y);
var imgd = context.getImageData(x, y, this.width, this.height);
var pix = imgd.data;
for (var i = 0, n = pix.length; i < n; i += 4) {
pix[i ] = 255 - pix[i ]; // red
pix[i+1] = 255 - pix[i+1]; // green
pix[i+2] = 255 - pix[i+2]; // blue
}
context.putImageData(imgd, x, y);
}, false);
img.src = 'test.jpg';
}, false);
// -->
</script>
And the 'Test.jpg' is on the same folder as the script. Am I missing anything? It displays the same image without inverting.
the answer to your question is right here
Tested on chrome, and it seem to be the file:// scheme that breaks it. When I moved the script to my local server (http://) instead of running the file (file://), it worked!
Proof:
I just tried that example and it seemed to work for me fine. I got a negative/inverted image rendered. Using Firefox 10.0.2