Javascript- canvas shooting positioning - javascript

I want to make a spaceship shoot in a game I am making, but I dont seem to be able to get the positions of the shots to load properly.
I have an array called shots[] and I want to push Shot() objects into it every N ticks when the player is holding down the mouse button.
So basically I want Shot() have x property equal to my ship.x property and y=ship.y.
Then I want it to have a Shot.dx property, which changes, depending on wether the cursor is above the middle of canvas or bellow (+3/-3) or left or right of the center (dy=+3/-3).
// Shooting
var shots = [] //Bullet array
//Object, which should be the position of the bullet.
function Shot() {
this.x=350
this.y=250;
this.dx=Shoot.x
this.dy=Shoot.y
}
//This keeps track of the cursor position in relation to the center of canvas.
function Shoot() {
started = false;
this.mousedown = function (ev) {
started = true;
};
this.mousemove = function (ev) {
if (started) {
if (ev.clientX>=350) this.x=3;
else this.x=-3;
if (ev.clientY>=250) this.y=3;
else this.y=-3;
}
};
this.mouseup = function (ev) {
started = false;
};
}
//The problem is, that when I set this.x in Shot() to be =ship.x the values
//dont get updated and it stays undefined after I push it into the array.
//Now I have it set to the center of canvas, but that it useless because it needs
//to move with the ship, but even now I am getting weird numbers and not the numbers
//that I actually put in. dx and dy dont get updated at all.
// Ship Model
var ship = {
x: 350,
y: 250,
lives: 3,
invulnerable: 0,
}
//Main function- pushes the objects into the array
function mainLoop() {
tick++
count()
interval()
chase()
console.log(started)
if (started && tick%20==0)
shots.push(new Shot());
keyboard()
display()
check()
requestAnimationFrame(mainLoop);
}
// Initialization
window.onload = function() {
// Setting variables
button = document.getElementById("button")
text = document.getElementById("text")
canvas = document.getElementById("canvas")
ctx = canvas.getContext("2d")
weapon = new Shoot();
canvas.onmousedown = weapon.mousedown;
canvas.onmousemove = weapon.mousemove;
canvas.onmouseup = weapon.mouseup;
requestAnimationFrame(mainLoop);
}
//I know this is a lot of code, but its all relevant. I am new to Javascript
//so I expect this to be some sort of a trivial error.
Here is a JSfiddle, but I dont know how that works so I cant get it to draw canvas, sorry: http://jsfiddle.net/JH3M6/

Here's an outline of how to fire your shots:
create an array to hold your shot objects
on mousedown: set the started flag (starts firing)
on mousedown: set the firing position for any new shot(s) to the current mouse position
on mousemove: reset the firing position for any new shot(s) to the current mouse position
on mouseup: clear the started flag (stops firing)
In the animation loop:
add a shot at the current firing position if the mouse is still down
move all shots by their dx,dy
remove any shots from the shots[] array if the shot has moved off-canvas
clear the screen and draw the shots in their new positions
A Demo: http://jsfiddle.net/m1erickson/2f9sf/
Here's example code:
<!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(){
// canvas related vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var bb=canvas.getBoundingClientRect();
var offsetX=bb.left;
var offsetY=bb.top;
ctx.fillStyle="blue";
// vars related to firing position and Shot(s)
var shotColor=ctx.fillStyle;
var started,mouseX,mouseY,dx,dy;
// Shots fired
var shots = [] //Bullet array
//Object, which should be the position of the bullet.
function Shot(x,y,dx,dy) {
this.x=x;
this.y=y;
this.dx=dx;
this.dy=dy;
}
Shot.prototype.display=function(){
// make fillstyle blue if it's not already blue
if(!ctx.fillStyle==shotColor){ctx.fillStyle=shotColor;}
// draw the shot on the canvas
ctx.fillRect(this.x,this.y,5,5);
}
// listen for mouse events
canvas.onmousedown=function(e){ started=true; setFirePosition(e); }
canvas.onmouseup=function(e){ started=false; }
canvas.onmousemove=function(e){
if(started){setFirePosition(e);}
}
// start the animation loop
requestAnimationFrame(animate);
// set the firing position of the next shot
function setFirePosition(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
dx=(mouseX>=canvas.width/2)?-3:3;
dy=(mouseY>=canvas.height/2)?-3:3;
}
// animation loop
// add shots if the mouse is down
// move shots until they move off-canvas
function animate(){
// request another frame
requestAnimationFrame(animate);
// if the mouse is down, add a shot
if(started){
shots.push(new Shot(mouseX,mouseY,dx,dy));
}
// if no work to do, return
if(shots.length==0){return;}
// new array of active shots
// "active" == shot has not moved off-canvas
var a=[];
// clear the canvas for this frame
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shots.length;i++){
// get a shot to process
var shot=shots[i];
// move this shot
shot.x+=shot.dx;
shot.y+=shot.dy;
// if the shot hasn't moved offscreen
// add the shot to "a" (the replacement shots array);
// draw this shot
if(shot.x>=0 && shot.x<=cw && shot.y>0 && shot.y<=ch){
a.push(shot);
shot.display();
}
}
// if shots went off-canvas, remove them from shots[]
if(a.length<shots.length){
shots.length=0;
Array.prototype.push.apply(shots,a);
}
}
}); // end $(function(){});
</script>
</head>
<body>
<h4>Mousedown to fire shots</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

Related

HTML Canvas Zoom to region on graph

just wondering has anyone got any solution for zooming into a region on a graph. When I say region I mean, you mouse down at a point on the canvas, move the mouse to another location while still pressed, and when mouse up I want to zoom into that block. and only scale the x axis, no the the y.
This is exactly what I want to do - http://canvasjs.com/docs/charts/basics-of-creating-html5-chart/zooming-panning/
Now I've been able to zoom in, but my graph is a mess after the zoom. Lines and points are all stretched, twisted etc, which I've tried to adjust using the scale.
This is what I've been doing so far.
let y = heightOfCanvas / 2,
// Get the region in pixels to zoom to. If canvas width
// is 500, this is lets say 100px to 400px.
regionToZoom = toX - fromX,
// Subtract from actual width, so we get what will be scaled
// out.
difference = widthOfCanvas - regionToZoom,
// Scale = fullWidth / partWidth
scale = widthOfCanvas / difference,
// This is how I worked out the value to translate back,
// so that it lines up right
translateBack = widthOfCanvas * scale
ctx.translate(from, y);
ctx.scale(scale, 1);
from = from * translateBack;
ctx.translate(-from, -y);
// REDRAW GRAPH AFTER
Like this zooms in fine, but as I said, it becomes a mess after.
If anyone has done something similar in the past, like in the example above, I would be so grateful for the help.
Sean.
Here's a quick example showing how to select & display a subset of a graph
// canvas vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.font='14px arial';
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
var isDrilled=false;
var isDown=false;
var startX,startY;
// graph vars
var axisY=ch/3;
var points=[];
// DEMO: add random data points
var demoPointCount=50;
for(var i=0;i<demoPointCount;i++){
points.push({y:Math.random()*150-75});
}
// draw the full data graph
draw(points,axisY);
// listen to mouse events
window.onmousedown=(function(e){handleMouseDown(e);});
window.onmousemove=(function(e){handleMouseMove(e);});
window.onmouseup=(function(e){handleMouseUpOut(e);});
window.onmouseout=(function(e){handleMouseUpOut(e);});
function draw(pts,axisY,startX,mouseX,drillStart,drillEnd){
// redraw the given pts data
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.moveTo(0,pts[0].y+axisY);
for(var i=0;i<pts.length;i++){
var x=cw/(pts.length-1)*i;
ctx.lineTo(x,pts[i].y+axisY);
}
ctx.stroke();
// used when highlighting a drilldown section of full data
if(startX && mouseX){
ctx.globalAlpha=0.10;
ctx.fillStyle='black';
ctx.fillRect(startX,0,mouseX-startX,ch);
ctx.globalAlpha=1.00;
}else if(drillStart && drillEnd){
ctx.fillText('Viewing '+drillStart+' - '+drillEnd+'. Click to return to all data view.',10,20);
}else{
ctx.fillText('Drag to select data to drill into.',10,20);
}
}
function handleMouseDown(e){
// if displaying drilled data, return to full data
if(isDrilled){
isDrilled=false;
draw(points,axisY);
return;
}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// start mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
isDown=true;
}
function handleMouseUpOut(e){
if(!isDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mouseup stuff here
isDown=false;
isDrilled=true;
// normalize
if(mouseX<startX){ var t=startX; startX=mouseX; mouseX=t; }
// fetch highlighted start & end data points
drillStart=parseInt(startX/cw*points.length);
drillEnd=parseInt(mouseX/cw*points.length);
var subset=points.slice(drillStart,drillEnd+1);
// draw the data subset
draw(subset,axisY,null,null,drillStart,drillEnd);
}
function handleMouseMove(e){
if(!isDown){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousemove stuff here
draw(points,axisY,startX,mouseX);
}
body{ background-color:white; }
#canvas{border:1px solid red; }
<canvas id="canvas" width=512 height=512></canvas>
[Previous outdated answer]
Here's a way to zoom without all the fuss about remembering transformations
This is a quick-thought so some tinkering may be needed.
Copy the graph by drawing it on a second in-memory canvas:
var memCanvas=myCanvas.cloneNode();
var memCtx=memCanvas.getContext('2d');
memCtx.drawImage(myCanvas,0,0);
DrawImage the memCanvas onto the displayed canvas:
context.drawImage(memCanvas,0,0);
On mousedown, grab the top-left coordinates of your selection box.
On mouseup, grab the bottom-right coordinates of your selection box. Note: you might have to flip top & bottom or left & right if top>bottom or left>right.
Calculate the scaling factor required to draw just the selection box full-frame into the visible canvas:
var scale = Math.min(
(myCanvas.width/selectionboxWidth),
(myCanvas.height/selectionboxHeight)
);
Use the clipping form of drawImage to pull just the selection box from the memCanvas and draw it scaled into the visible canvas:
var x=sboxLeft;
var y=sboxTop;
var w=sboxRight-sboxLeft;
var h=sboxBottom-sboxTop;
sboxcontext.drawImage(
memCanvas, // fetch from the memCanvas
x,y,w,h, // clip the selected box
0,0,w*scale,h*scale); // scale the selected box into the visible canvas
Then when you want to unzoom you just redraw the full memCanvas to the visible canvas (no need to untransform). The resulting graph is not a mess!
context.drawImage(memCanvas,0,0); // unzoomed -- simply!

draggable objects and canvas in loop

I have three questions. The first question is the most important but I appreciate to get answers to the others.
First question, see this jsfiddle:
https://jsfiddle.net/owLdgrdq/10
//copia nariz
$(document).on('click', '#add-nariz', function(){
var a = $('#add-nariz');
var src = a.attr('src');
var elem = $('<img class="objetos" src="' + src + '" width="30px" height="30px" style="positon: relative;" />');
$('#fotoAlterada').append(elem);
elem.draggable();
});
//copia bolinha azul
$(document).on('click', '#add-bb', function(){
var a = $('#add-bb');
var src = a.find('img:first').attr('src');
var elem = $('<img class="objetos" src="' + src + '" width="30px" height="30px" style="positon: relative;" />');
$('#fotoAlterada').append(elem);
elem.draggable();
});
$(document).on('click', '#salvaImagem', function(){
var m = $('#foto');
var totX = parseInt(m.css('width'));
var totY = parseInt(m.css('height'));
var c = document.getElementById('myCanvas');
c.width = totX;
c.height = totY;
var ctx = c.getContext('2d');
alert(totX + '\n' + totY);
var base = document.getElementById('foto');
ctx.drawImage(base,0,0,totX,totY,0,0,totX,totY);
var posicoes = [];
$(".objetos").each(function(){
var img = $(this);
x = parseInt(img.css("left"))+totX;
y = parseInt(img.css("top"))+totY;
altura = parseInt(img.css("width"));
largura = parseInt(img.css("height"));
posicoes.push([
x,
y,
largura,
altura
]);
});
alert( JSON.stringify(posicoes));
var j;
var numAderecos = posicoes.length;
for(j = 0; j < numAderecos; j++){
ctx.drawImage(base,posicoes[j][0],posicoes[j][1],posicoes[j][2],posicoes[j][3]);
}
});
I have some icons (Adicionar isto:) what I want add to a photo (Editar isto:). When the user clicks on the icons, a copy is made beside the photo. More click make more copies. These copies are draggable. The user choose the position of the objects and click in a button to save ("Salvar Mudanças"). This button makes a copy of the original photo with the new objects on it (inside a canvas).
At final of the JS code I put little photos (same as original) as little objects added to the photo in the canvas, but just because I don't know how to copy each object added. How can I do that?
Second question, when click on the button, the positions are not the same in the canvas, they are pull to down a little (and left too). Why is this happening if I put padding and margin 0px in CSS code?
Third question, the variable 'base' I used $('#foto') to get by id, but I can't use it at canvas arguments. When I use document.getElementById('foto') I can. What is the difference among them?
----editing ----
My aim with this is to make a mobile application, through cordova/phonegap. The user must be able to get a picture from the device or a selfie with the cam (it's already ok). When the picture/photo is gotten, some new objects like a crown's nose, colorful hat and other silly things appear on the window (it's ok, just change style="display: none;" to be displayed), when the user clicks on these things they make a copy at the bottom of the photo and these copies must be draggable (it's nice too). Then, with jQuery-UI I'm getting the position of all dragged objects into the photo, the user clicks on a button "Salvar Mudanças" ("save changes"), and this action copy the photo plus objects to a canvas area (this is the way I got to make the app). After, the user clicks on another button to share the modified image (in canvas) through whatsapp, facebook, email and any other way which the device could do.
Here is an alternate way of appending icons onto an image using just a canvas
Carve out a toolbar area at the top of an html5 canvas and fill it with your desired icons.
Put the destination image below the toolbar.
Listen for mouse events.
Allow the user to icons from the toolbar onto the image below.
When the user "drops" the dragging icon by releasing the mouse, create a duplicate of the dragged icon at the dropped position.
Here is annotated code and a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }
var isDown=false;
var startX,startY;
ctx.lineWidth=0.50;
ctx.strokeStyle='lightgray';
ctx.fillStyle='white';
// vars for icons, etc
var tbar={width:cw,height:35}
var tw=30;
var th=30;
var dragging=null;
var dropped=[];
var icons=[
{name:'Flower',x:tw*0,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/flower.png'},
{name:'Star',x:tw*1,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/star.png'},
{name:'Plane',x:tw*2,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/plane2.png'},
{name:'Mario',x:tw*3,y:2,url:'https://dl.dropboxusercontent.com/u/139992952/multple/marioStanding.png'},
];
var thumbs=[];
var mainImg=new Image();
mainImg.crossOrigin='anonymous';
mainImg.onload=start;
mainImg.src='https://dl.dropboxusercontent.com/u/139992952/multple/husky.jpg';
var imgCount=icons.length+1;
for(var i=0;i<icons.length;i++){
var icon=icons[i];
icon.index=i;
icon.img=new Image();
icon.img.crossOrigin='anonymous';
icon.img.onload=start;
icon.img.src=icon.url;
}
// start is called when each image is fully loaded
function start(){
// wait for all images to load
if(--imgCount>0){return;}
// create
for(var i=0;i<icons.length;i++){
var icon=icons[i];
thumbs.push(thumb(icon.img,tw,th));
}
// draw the toolbar & image
draw();
// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});
}
// create thumbnails for each image (to be used as icons in the toolbar)
function thumb(img,w,h){
var iw=img.width;
var ih=img.height;
var s=Math.min((w/iw),(h/ih))
var c=document.createElement('canvas');
c.width=iw*s;
c.height=ih*s;
c.getContext('2d').drawImage(img,0,0,iw,ih,0,0,iw*s,ih*s);
return(c);
}
// draw the toolbar, image & any clone thumbnails added to the image
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.fillRect(0,0,cw,tbar.height);
ctx.strokeRect(0,0,cw,tbar.height);
ctx.drawImage(mainImg,0,tbar.height);
for(var i=0;i<icons.length;i++){
var icon=icons[i];
ctx.drawImage(thumbs[icon.index],icon.x,icon.y);
}
for(var i=0;i<dropped.length;i++){
var icon=dropped[i];
ctx.drawImage(thumbs[icon.thumbIndex],icon.x,icon.y);
}
if(dragging){
ctx.drawImage(thumbs[dragging.thumbIndex],dragging.x,dragging.y);
}
}
//
function handleMouseDown(e){
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get the mouse position
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
// hit test the toolbar icons and any dropped icons
var mx=startX;
var my=startY;
// hit test toolbar icons
if(mx>0 && mx<tw*icons.length && my>2 && my<2+tw){
var icon=icons[parseInt(mx/tw)];
dragging={thumbIndex:icon.index,x:icon.x,y:icon.y};
dragging.source='icons';
return;
}
// hit test dropped icons
for(var i=0;i<dropped.length;i++){
var icon=dropped[i];
if(mx>icon.x && mx<icon.x+tw && my>icon.y && my<icon.y+th){
dragging=dropped[i];
dragging.source='dropped';
dragging.droppedIndex=i;
return;
}
}
}
// Add any
function handleMouseUpOut(e){
if(!dragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
//
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// add the icon to its dropped position
if(dragging.source=='icons'){
if(dragging.y>tbar.height){
dropped.push(dragging);
}
// remove the dropped icon if it's back in the toolbar
}else if(dragging.y<tbar.height){
dropped.splice(dragging.droppedIndex,1);
}
// clear dragging
dragging=null;
// redraw
draw();
}
// move any icon that's being dragged
function handleMouseMove(e){
if(!dragging){return;}
// tell the browser we're handling this event
e.preventDefault();
e.stopPropagation();
// get mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// move the dragged icon by the distance the mouse
// has moved since the last mousemove event
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
dragging.x+=dx;
dragging.y+=dy;
// redraw
draw();
}
// save just the image with dropped icons
$('#save').click(function(){
var c=document.createElement('canvas');
c.width=cw;
c.height=ch-tbar.height;
c.getContext('2d').drawImage(canvas,0,tbar.height,cw,ch-tbar.height,0,0,c.width,c.height);
var img=new Image();
img.onload=function(){
document.body.appendChild(img);
}
img.src=c.toDataURL();
});
body{ background-color: ivory; }
#canvas{border:1px solid lightgray; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<button id='save'>Save</button>
<h4>Drag from top toolbar & icon will be drop-copied<br>Drag dropped icons around image<br>Drag dropped icon back to toolbar to delete.</h4>
<canvas id="canvas" width=300 height=300></canvas>
It's a little tough to follow, but I suspect you're asking about compositing all of the existing DOM images onto a single canvas context. For that you'll probably want to look at the globalCompositeOperation property on the 2dcanvas:
globalCompositeOperation - the property
Compositing Tutorial - basic outline of how the different types work
Also your 3rd question: $('#foto') returns a jquery object, and document.getElementById returns a DOM object. To get the actual DOM element you would do something like: var theEl = $('#foto')[0]

html5 canvas drawing: fast motion with mouse cancels line before it hits the edge of the canvas

The app works fine so far except if the line is drawn really fast and leaves the edge of the canvas, the line is then not drawn to the edge of the canvas. There is a part missing from it.
I'm trying to fix the issue with:
canvasVar.addEventListener ('mouseout', clearPathIfMouseCursorLeavesCanvasFunc);
and
function clearPathIfMouseCursorLeavesCanvasFunc(e){
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
mouseButtonHeld = false;
I've tried some things like adding a settimeout(); but nothing worked so far. I don't know what causes this and I've been searching if someone else had this problem and a fix for it, but every canvas drawing app I've come across has the same issues.
It's very important that the line is drawn to the edge and that the users mouse motion is recognized, not just a line to the last coordinates where the mouse left the canvas.
It's been days now that I'm stuck with this problem. Help is really appreciated!
Whole Code:
// Varibale declaration
var canvasVar = document.getElementById('canvasHtmlElement');
var contextVar = canvasVar.getContext('2d');
var pointRadiusVar = 0.5;
var mouseButtonHeld = false;
var pointsArrPosition = 0;
//Arrays
var pointsArr = [];
// Varibale declration end
//canvas setup
canvasVar.width = window.innerWidth;
canvasVar.height = window.innerHeight;
//canvas setup end
//resize fix
window.onresize = function() {
var tempImageVar = contextVar.getImageData(0, 0, canvasVar.width, canvasVar.height);
canvasVar.width = window.innerWidth;
canvasVar.height = window.innerHeight;
contextVar.putImageData(tempImageVar, 0, 0);
}
//resize fix end
//functions
// Objects
function pointObject() {
this.x = 0;
this.y = 0;
this.fill = '#444444';
}
function addFilledCircleFunc(x, y) {
//alert('works1');
var filledCircle = new pointObject;
filledCircle.x = x;
filledCircle.y = y;
pointsArr.push(filledCircle);
contextVar.lineWidth = 10; //pointRadiusVar * 2; // Line Width
contextVar.lineTo(pointsArr[pointsArrPosition].x, pointsArr[pointsArrPosition].y);
contextVar.stroke();
//contextVar.beginPath();
contextVar.fillRect(filledCircle.x, filledCircle.y, 1, 1);
//contextVar.arc(filledCircle.x, filledCircle.y, pointRadiusVar, 0, Math.PI * 2);
//contextVar.fill();
//contextVar.lineWidth = 0.5;
//contextVar.stroke();
//contextVar.beginPath();
pointsArrPosition++;
//contextVar.moveTo(pointsArr[pointsArrPosition].x, pointsArr[pointsArrPosition].y);
//alert(pointsArr[0].x);
}
//Objects end
// create circle on mouse clicked point while mousebutton is held
var addPointToCanvasVar = function(e) {
if (mouseButtonHeld) {
//alert('addpointfunc');
addFilledCircleFunc(e.clientX, e.clientY);
}
};
// MAKE SURE that lines work when drawn over the edge of the canvas
function clearPathIfMouseCursorLeavesCanvasFunc(e) {
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
mouseButtonHeld = false;
}
// end
// mouse Up/Down functions
var mouseDownVar = function(e) {
//alert("mouseDown");
addPointToCanvasVar(e); // add point on first click, not just when mousebutton is held
mouseButtonHeld = true;
}
var mouseUpVar = function() {
//alert("mouseUp");
mouseButtonHeld = false;
contextVar.beginPath(); // clears the path so buttonpresses dont connect the line
}
// mouse Up/Down Switch end
//functions end
//listeners
canvasVar.addEventListener('mousemove', addPointToCanvasVar);
canvasVar.addEventListener('mouseup', mouseUpVar);
canvasVar.addEventListener('mousedown', mouseDownVar);
canvasVar.addEventListener('mouseout', clearPathIfMouseCursorLeavesCanvasFunc);
//listeners end
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Confident Drawing</title>
</head>
<body style="margin: 0">
<canvas id="canvasHtmlElement" style="display: block;">
Your Browser does not support Canvas! Please update to a newer version.
</canvas>
<script src="main_0.06.js"></script>
</body>
</html>
If you don't get what I mean: Run the snippet and draw a line as fast as you can while exiting the canvas.
The reason the line ends prematurely near the edge when you quickly draw a line across the edge is because the last mousemove event fired when the mouse was still in the canvas and just short of the edge, and the very next mousemove event fired after your mouse left the canvas. To fix that problem, simply draw your line from the last recorded mouse position in the canvas to the one outside of the canvas as soon as the mouseout event fires.
You can add a new global variable mousePosition and initialize it to {x:0,y:0}. Every time mousemove fires (whenever you call addPointToCanvasVar), record the e.clientX and e.clientY to your mousePosition. Then when mouseout fires (whenever you call clearPathIfMouseCursorLeavesCanvasFunc), draw the rest of the line from mousePosition to the current e.clientX and e.clientY position. This will complete the line to the end of the canvas edge.

How to create your own interactive panoramic website

I want to make a website that will be a room, and I want users to be able to look at that room in limited panoramic view, e.g. up/down 30 degrees, left/right 45 degrees, and I want to put objects in that panoramic view that user could interact with.
I have found that google street view could give the panoramic effect, but I'm not quite sure if it would suit my needs as I would want to put objects in it.
Are there any alternative panoramic libraries that are good and could give me tools to support what I want to achieve?
You are basically talking about panning around a view.
You can do that by drawing the view with horizontal & vertical offsets.
Here's annotated code and a Demo: http://jsfiddle.net/m1erickson/32Y5A/
<!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:20px;}
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.strokeStyle="red";
ctx.lineWidth=5;
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var lastX=0;
var lastY=0;
var panX=0;
var panY=0;
var dragging=[];
var isDown=false;
// create "draggable" rects
var images=[];
images.push({x:200,y:150,width:25,height:25,color:"green"});
images.push({x:80,y:235,width:25,height:25,color:"gold"});
// load the tiger image
var tiger=new Image();
tiger.onload=function(){
draw();
}
tiger.src="https://dl.dropboxusercontent.com/u/139992952/stackoverflow/tiger.png";
function draw(){
ctx.clearRect(0,0,canvas.width,canvas.height);
// draw tiger
ctx.globalAlpha=0.25;
ctx.drawImage(tiger,panX,panY,tiger.width,tiger.height);
// draw color images
ctx.globalAlpha=1.00;
for(var i=0;i<images.length;i++){
var img=images[i];
ctx.beginPath();
ctx.rect(img.x+panX,img.y+panY,img.width,img.height);
ctx.fillStyle=img.color;
ctx.fill();
ctx.stroke();
}
}
// create an array of any "hit" colored-images
function imagesHitTests(x,y){
// adjust for panning
x-=panX;
y-=panY;
// create var to hold any hits
var hits=[];
// hit-test each image
// add hits to hits[]
for(var i=0;i<images.length;i++){
var img=images[i];
if(x>img.x && x<img.x+img.width && y>img.y && y<img.y+img.height){
hits.push(i);
}
}
return(hits);
}
function handleMouseDown(e){
// get mouse coordinates
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// set the starting drag position
lastX=mouseX;
lastY=mouseY;
// test if we're over any of the images
dragging=imagesHitTests(mouseX,mouseY);
// set the dragging flag
isDown=true;
}
function handleMouseUp(e){
// clear the dragging flag
isDown=false;
}
function handleMouseMove(e){
// if we're not dragging, exit
if(!isDown){return;}
//get mouse coordinates
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// calc how much the mouse has moved since we were last here
var dx=mouseX-lastX;
var dy=mouseY-lastY;
// set the lastXY for next time we're here
lastX=mouseX;
lastY=mouseY;
// handle drags/pans
if(dragging.length>0){
// we're dragging images
// move all affected images by how much the mouse has moved
for(var i=0;i<dragging.length;i++){
img=images[dragging[i]];
img.x+=dx;
img.y+=dy;
}
}else{
// we're panning the tiger
// set the panXY by how much the mouse has moved
panX+=dx;
panY+=dy;
}
draw();
}
// use jQuery to handle mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag the tiger and<br>independently drag the rectangles.</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>

To drag shapes drawn on canvas same as paint

I have tool similar to paint where i allow user to draw different shapes on canvas using mouse events.I want to allow the user to drag the shapes(same as paint) once they are drawn on canvas. Did anyone done like this before? I have already tried using OOPDragging but that didn't work in my case.Also my tool include all kind of shapes like Line,elbow connector,oval,text,image and not just circles and rectangles. Can anyone please suggest some easy to achieve solution for this as i need it ASAP.
Thanks in advance.
A Demo: http://jsfiddle.net/m1erickson/JrzM2/
Assume you've created this triangle in your paint program
And the points for that triangle are in an array like this:
[{x:0,y:20},{x:30,y:0},{x:70,y:45}]
To move that triangle to [20,35], you need to first offset the triangle by x:20 & y:35
var myTriangle={
x:20,
y:35,
points:[{x:0,y:20},{x:30,y:0},{x:70,y:45}]
}
Then you can draw the triangle at [20,35] like this:
Note that the offsets (20,35) are added to each point in the triangle's position
function draw(myTriangle){
var x=myTriangle.x;
var y=myTriangle.y;
var points=myTriangle.points;
ctx.beginPath();
ctx.moveTo( x+points[0].x, y+points[0].y );
for(var i=1;i<points.length;i++){
ctx.lineTo( x+points[i].x, y+points[i].y );
}
ctx.closePath();
ctx.fill();
}
To drag the triangle, you listen for mouse events
In mousedown, check if the mouse is over the triangle. If yes, start the drag.
In mousemove, add the distance the user dragged since the last mousemove to the triangle's position.
In mouseup, stop the drag
In mousedown
Canvas has a nice built-in function to test if any specified point is inside a path like the triangle.
This function is context.isPointInPath(mouseX,mouseY) and it tests if mouseX/mouseY is inside the last drawn path.
If the mouse was pressed over the triangle, we set the isSelected flag to indicate the triangle should be dragged with every mousemove.
So the mousedown function looks like this:
function handleMouseDown(e){
// tell the browser we're using mousedown,
// so don't bother doing any browser stuff with this event
e.preventDefault();
// get the current mouseX,mouseY position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// test if mouseX,mouseY is inside the triangle
if(ctx.isPointInPath(startX,startY)){
// if yes, set the "isSelected" flag
// which indicates that the triangle should
// move with the mouse
isSelected=true;
}
}
In mousemove
The mousemove event is triggered about 20-30 times per second as the user moves the mouse.
In mousemove, if the triangle isSelected, we want to calculate how far the mouse has moved since the last mousemove event.
Then we want to change the x,y position of the triangle by the distance that the mouse has moved.
So the mousemove function looks like this:
function handleMouseMove(e){
// if the triangle wasn't selected during mousedown
// there's nothing to do, so just return
if(!isSelected){return;}
e.preventDefault();
// get the current mouse position
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// calculate how far the mouse has moved since the last mousemove
var dx=mouseX-startX;
var dy=mouseY-startY;
// for next mousemove, reset the starting XY to the current XY
startX=mouseX;
startY=mouseY;
// move the triangle by the change in mouse position
myTriangle.x+=dx;
myTriangle.y+=dy;
// clear the canvas and
// redraw the triangle at its new position
context.clearRect(0,0,canvas.width,canvas.height);
draw(myTriangle);
}
In mouseup
In mouseup, the isSelected flag is cleared since the drag is over:
function handleMouseUp(e){
e.preventDefault();
isSelected=false;
}
Here's code for a more complex example with multiple shapes:
<!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(){
// vars related to canvas
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var $canvas=$("#canvas");
var canvasOffset=$canvas.offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var scrollX=$canvas.scrollLeft();
var scrollY=$canvas.scrollTop();
var cw=canvas.width;
var ch=canvas.height;
// vars related to dragging
var isDown=false;
var startX;
var startY;
// save your shape-points in
var shapes=[];
var selectedShape=null;
// test shapes
addShape(50,50,[{x:0,y:20},{x:30,y:0},{x:70,y:45}],"blue","red");
addShape(100,100,
[{x:0,y:10},{x:30,y:10},{x:30,y:0},
{x:45,y:15},{x:30,y:30},{x:30,y:20},{x:0,y:20}],
"green","red");
// begin...
drawAll();
function addShape(x,y,points,fill,stroke){
shapes.push({x:x,y:y,points:points,fill:fill,stroke:stroke});
}
function define(shape){
var x=shape.x;
var y=shape.y;
var points=shape.points;
ctx.beginPath();
ctx.moveTo(x+points[0].x,y+points[0].y);
for(var i=1;i<points.length;i++){
ctx.lineTo(x+points[i].x,y+points[i].y);
}
ctx.closePath();
}
function draw(shape){
define(shape);
ctx.fillStyle=shape.fill;
ctx.fill();
ctx.strokeStyle=shape.stroke;
ctx.stroke();
}
function drawAll(){
ctx.clearRect(0,0,cw,ch);
for(var i=0;i<shapes.length;i++){
draw(shapes[i]);
}
}
function handleMouseDown(e){
e.preventDefault();
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
for(var i=0;i<shapes.length;i++){
define(shapes[i]);
if(ctx.isPointInPath(startX,startY)){
selectedShape=shapes[i];
isDown=true;
}
}
}
function handleMouseUp(e){
e.preventDefault();
isDown=false;
selectedShape=null;
}
function handleMouseOut(e){
e.preventDefault();
isDown=false;
selectedShape=null;
}
function handleMouseMove(e){
if(!isDown){return;}
e.preventDefault();
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
var dx=mouseX-startX;
var dy=mouseY-startY;
startX=mouseX;
startY=mouseY;
selectedShape.x+=dx;
selectedShape.y+=dy;
drawAll();
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
}); // end $(function(){});
</script>
</head>
<body>
<h4>Drag the shapes around the canvas</h4>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I had the same problem some time ago, but I only needed lines, but I think you can use my solution for all the different shapes.
Basically you'll need to overlapping canvases, one that listens to the mouse events and draws the temporary shape, once the user releases the mouse button, you get the values and draw the shape on the original canvas. Here's a link to what I did: http://www.zhereicome.com/experiments/statics/drawtogether/
and the main js file:
http://www.zhereicome.com/experiments/statics/drawtogether/js/main.js
The important part is:
temp_canvas.onmousemove = function(e) {
//console.log(e);
if ( self.mouseDown ) {
self.drawTempLine(e.clientX, e.clientY);
}
}
temp_canvas.onmousedown = function(e) {
start = {
x: e.clientX,
y: e.clientY
}
self.mouseDown = true;
}
temp_canvas.onmouseup = function(e) {
end = {
x: e.clientX,
y: e.clientY
}
self.mouseDown = false;
self.sendToServer(); //When the user has finished drawing the line, send it to the server
}
I have a function sendToServer() because my project was a realtime multiplayer drawing app. In your case you'll have to replace it with a drawOnFinalCanvas();

Categories