Javascript canvas hover over a tile - javascript

I have a canvas which I am drawing from certain tiles (new Image()), lets say there are for example 10x10 tiles in my canvas.
Every tile has its own data I want to display "popup" with information about this tile everytime user hovers over the tile with mouse, it should be following the mouse until user leaves the tile.
Example of what I want to achieve would be google maps, but it should be following the mouse as I move the mouse over the tile:
https://i.gyazo.com/d32cd5869ae9b2e0d9a7053729e2d2aa.mp4

You will need three things to achieve this:
A way to draw the background image on your canvas
A way to track mouse position over the canvas
A list of "zones" and a way to determine which zone is "triggered"
Here is an implementation of the above points:
var GridOverlay = /** #class */ (function () {
/**
* Creates an instance of GridOverlay.
* #param {string} imageSrc
* #param {{
* position: IPoint,
* size: IPoint,
* message: string
* }[]} [zones=[]]
* #memberof GridOverlay
*/
function GridOverlay(imageSrc, zones) {
if (zones === void 0) { zones = []; }
var _this = this;
this.zones = zones;
/**
* The last registered position of the cursor
*
* #type {{ x: number, y: number }}
* #memberof GridOverlay
*/
this.mousePosition = { x: 0, y: 0 };
//Create an image element
this.img = document.createElement("img");
//Create a canvas element
this.canvas = document.createElement("canvas");
//When the image is loaded
this.img.onload = function () {
//Scale canvas to image
_this.canvas.width = _this.img.naturalWidth;
_this.canvas.height = _this.img.naturalHeight;
//Draw on canvas
_this.draw();
};
//Set the "src" attribute to begin loading
this.img.src = imageSrc;
//Listen for "mousemove" on our canvas
this.canvas.onmousemove = this.mouseMove.bind(this);
}
/**
* Registers the current position of the cursor over the canvas, saves the coordinates and calls "draw"
*
* #param {MouseEvent} evt
* #memberof GridOverlay
*/
GridOverlay.prototype.mouseMove = function (evt) {
this.mousePosition.x = evt.offsetX;
this.mousePosition.y = evt.offsetY;
this.draw();
};
/**
* Clears and redraws the canvas with the latest data
*
* #memberof GridOverlay
*/
GridOverlay.prototype.draw = function () {
//Get drawing context
var ctx = this.canvas.getContext("2d");
//Clear canvas
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
//Draw background
ctx.drawImage(this.img, 0, 0);
//Loop through zones
for (var zoneIndex = 0; zoneIndex < this.zones.length; zoneIndex++) {
var zone = this.zones[zoneIndex];
//Check for cursor in zone
if (zone.position.x <= this.mousePosition.x &&
zone.position.x + zone.size.x >= this.mousePosition.x &&
zone.position.y <= this.mousePosition.y &&
zone.position.y + zone.size.y >= this.mousePosition.y) {
//Display zone message on cursor position
ctx.fillText(zone.message, this.mousePosition.x, this.mousePosition.y);
//Break so that we only show a single message
break;
}
}
return this;
};
return GridOverlay;
}());
//TEST
var grid = new GridOverlay("https://placeholdit.imgix.net/~text?txtsize=60&txt=1&w=500&h=500", [
{ message: "Zone 1", position: { x: 10, y: 10 }, size: { x: 100, y: 100 } },
{ message: "Zone 2", position: { x: 80, y: 80 }, size: { x: 100, y: 100 } },
]);
document.body.appendChild(grid.canvas);

Related

I am not able to create a cloth example in matter.js

Please help me out as I am not able to run the example code from matter. When I use this code with <html>, it shows a blank screen. There is no tutorial for making a cloth simulation in google or youtube. It would be appreciated if you would help. I use sublime text for editing.
var Example = Example || {};
Example.cloth = function() {
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Body = Matter.Body,
Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse,
Composite = Matter.Composite,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create(),
world = engine.world;
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600
}
});
Render.run(render);
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
// see cloth function defined later in this file
var cloth = Example.cloth.cloth(200, 200, 20, 12, 5, 5, false, 8);
for (var i = 0; i < 20; i++) {
cloth.bodies[i].isStatic = true;
}
Composite.add(world, [
cloth,
Bodies.circle(300, 500, 80, { isStatic: true, render: { fillStyle: '#060a19' }}),
Bodies.rectangle(500, 480, 80, 80, { isStatic: true, render: { fillStyle: '#060a19' }}),
Bodies.rectangle(400, 609, 800, 50, { isStatic: true })
]);
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.98,
render: {
visible: false
}
}
});
Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
// fit the render viewport to the scene
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: 800, y: 600 }
});
// context for MatterTools.Demo
return {
engine: engine,
runner: runner,
render: render,
canvas: render.canvas,
stop: function() {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
};
Example.cloth.title = 'Cloth';
Example.cloth.for = '>=0.14.2';
/**
* Creates a simple cloth like object.
* #method cloth
* #param {number} xx
* #param {number} yy
* #param {number} columns
* #param {number} rows
* #param {number} columnGap
* #param {number} rowGap
* #param {boolean} crossBrace
* #param {number} particleRadius
* #param {} particleOptions
* #param {} constraintOptions
* #return {composite} A new composite cloth
*/
Example.cloth.cloth = function(xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {
var Body = Matter.Body,
Bodies = Matter.Bodies,
Common = Matter.Common,
Composites = Matter.Composites;
var group = Body.nextGroup(true);
particleOptions = Common.extend({ inertia: Infinity, friction: 0.00001, collisionFilter: { group: group }, render: { visible: false }}, particleOptions);
constraintOptions = Common.extend({ stiffness: 0.06, render: { type: 'line', anchors: false } }, constraintOptions);
var cloth = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function(x, y) {
return Bodies.circle(x, y, particleRadius, particleOptions);
});
Composites.mesh(cloth, columns, rows, crossBrace, constraintOptions);
cloth.label = 'Cloth Body';
return cloth;
};
if (typeof module !== 'undefined') {
module.exports = Example.cloth;
}
If you are looking to just get something on the screen, the fastest way is to use matter.js from a CDN.
You are seeing a blank screen because the example is meant to be bundled using a tool like Webpack or Parcel with other javascript files which would call the cloth() function. So all you need to do is to add Example.cloth(); at the bottom of the example code and it will execute upon loading the page.
The snippet below goes in the body tag of your HTML page which you can then open in your browser.
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js" integrity="sha512-5T245ZTH0m0RfONiFm2NF0zcYcmAuNzcGyPSQ18j8Bs5Pbfhp5HP1hosrR8XRt5M3kSRqzjNMYpm2+it/AUX/g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
var Example = Example || {};
Example.cloth = function () {
var Engine = Matter.Engine,
Render = Matter.Render,
Runner = Matter.Runner,
Body = Matter.Body,
Composites = Matter.Composites,
MouseConstraint = Matter.MouseConstraint,
Mouse = Matter.Mouse,
Composite = Matter.Composite,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create(),
world = engine.world;
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600
}
});
Render.run(render);
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
// see cloth function defined later in this file
var cloth = Example.cloth.cloth(200, 200, 20, 12, 5, 5, false, 8);
for (var i = 0; i < 20; i++) {
cloth.bodies[i].isStatic = true;
}
Composite.add(world, [
cloth,
Bodies.circle(300, 500, 80, { isStatic: true, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(500, 480, 80, 80, { isStatic: true, render: { fillStyle: '#060a19' } }),
Bodies.rectangle(400, 609, 800, 50, { isStatic: true })
]);
// add mouse control
var mouse = Mouse.create(render.canvas),
mouseConstraint = MouseConstraint.create(engine, {
mouse: mouse,
constraint: {
stiffness: 0.98,
render: {
visible: false
}
}
});
Composite.add(world, mouseConstraint);
// keep the mouse in sync with rendering
render.mouse = mouse;
// fit the render viewport to the scene
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: 800, y: 600 }
});
// context for MatterTools.Demo
return {
engine: engine,
runner: runner,
render: render,
canvas: render.canvas,
stop: function () {
Matter.Render.stop(render);
Matter.Runner.stop(runner);
}
};
};
Example.cloth.title = 'Cloth';
Example.cloth.for = '>=0.14.2';
/**
* Creates a simple cloth like object.
* #method cloth
* #param {number} xx
* #param {number} yy
* #param {number} columns
* #param {number} rows
* #param {number} columnGap
* #param {number} rowGap
* #param {boolean} crossBrace
* #param {number} particleRadius
* #param {} particleOptions
* #param {} constraintOptions
* #return {composite} A new composite cloth
*/
Example.cloth.cloth = function (xx, yy, columns, rows, columnGap, rowGap, crossBrace, particleRadius, particleOptions, constraintOptions) {
var Body = Matter.Body,
Bodies = Matter.Bodies,
Common = Matter.Common,
Composites = Matter.Composites;
var group = Body.nextGroup(true);
particleOptions = Common.extend({ inertia: Infinity, friction: 0.00001, collisionFilter: { group: group }, render: { visible: false } }, particleOptions);
constraintOptions = Common.extend({ stiffness: 0.06, render: { type: 'line', anchors: false } }, constraintOptions);
var cloth = Composites.stack(xx, yy, columns, rows, columnGap, rowGap, function (x, y) {
return Bodies.circle(x, y, particleRadius, particleOptions);
});
Composites.mesh(cloth, columns, rows, crossBrace, constraintOptions);
cloth.label = 'Cloth Body';
return cloth;
};
Example.cloth();
</script>

zoom by mouse wheel on canvas Angular+D3

I want to make zoom on HTML canvas by mouse wheel like on google map.
I have two canvases on my project, one for showing an image and one for edit the image. I do for both of the canvases transformation and scale according to the mouse position.
Everything goes fine. the zoom and the draw after the zoom. the problem is when I try to save the original position of the drawing after the zoom - the position is not correct.
HTML:
<canvas id="backgroundCanvas" #backgroundCanvas class="background-canvas" width="930" height="710"></canvas>
<canvas #editCanvas mouseWheel class="edit-canvas" width="930" height="710" [style.cursor]="editMode ? 'pointer' : '-webkit-grab'" (mouseWheelUp)="mouseWheelUpFunc($event)" (mouseWheelDown)="mouseWheel($event)"></canvas>
JS:
this.canvas1 = d3.select("#editCanvas");
this.context = this.canvas1.node().getContext("2d");
this.width = this.canvas1.property("width");
this.height = this.canvas1.property("height");
this.canvas1.call(d3.zoom()
.scaleExtent([1, 4])
.on("zoom", () => {
if (this.zoomStep === 0) {
this.isZoomed = false; //set isZoomed to false for disable to drag the image
}
var wheel = d3.event.sourceEvent.wheelDelta/120;//n or -n
var zoom = 0;
if(wheel < 0)//if it's zoom out
{
this.zoomStep--;
this.setCurrentScale(this.zoomFactor, false);
this.resetBackgroundTopLeft();
console.log("wheel is under 0 ");
zoom = 1/1.05;
}
else//if it's zoom in
{
this.zoomStep++;
this.isZoomed = true;//set isZoomed to true for enable to drag the image
this.setCurrentScale(this.zoomFactor, true);
zoom = 1.05;
}
this.currentzoom *= zoom; //set the current zoom for know whem to stop
this.clearBackgroundCanvas(); // clear the background image befor draw it again.
/*********************************change the background canvas (where the image is)****************************************************/
this.backgroundContext.save();
this.backgroundContext.translate(
d3.event.transform.x,
d3.event.transform.y
);
this.backgroundContext.scale(d3.event.transform.k, d3.event.transform.k);
this.backgroundContext.drawImage(this.curentImage, this.imageTopLeft.x, this.imageTopLeft.y ,this.img.w * this.currentScale, this.img.h * this.currentScale);
this.backgroundContext.restore();
/***********************************change the edit canvas (where the tags are)**************************************************/
this.editContext.save();
this.editContext.translate(
d3.event.transform.x,
d3.event.transform.y
);
this.editContext.scale(d3.event.transform.k, d3.event.transform.k);
this.draw() //clear and then draw the marker again
this.editContext.restore();
/*************************************************************************************/
}))
.on("mousedown.zoom", () =>{
this.onMouseDown1(d3.event);
})
/**This function set the sacle to zoom In or Out according the mouse wheel*/
setCurrentScale = (scale: number, zoomIn: boolean) => {
this.currentScale *= zoomIn ? scale : 1 / scale;
}
/**This function clear all the canvase area */
clearBackgroundCanvas() {
this.backgroundContext.clearRect(0, 0, this.canvas.w, this.canvas.h);
}
onMouseDown1(evt: MouseEvent) {
var coordinates = d3.mouse(this.canvas1.node());
console.log("onMouseDown" + evt.offsetX , evt.offsetY ,coordinates)
if (this.editMode) {
if (this.isInDrawingArea(evt.offsetX, evt.offsetY, this.imageTopLeft)) {
// return relative mouse position
this.currentDrawnMarkerStart = {
x: coordinates[0],
y: coordinates[1]
};
this.isDrawingRect = true;
if(this.scale == null)
this.scale ={k :1 };
this.drawRectBorder(
this.currentDrawnMarkerStart.x ,
this.currentDrawnMarkerStart.y ,
250*this.currentScale*this.scale.k,
250*this.currentScale*this.scale.k,
'#004de6',2*this.scale.k);//color for the rect tag on drawing
const m = {
x: this.currentDrawnMarkerStart.x,
y: this.currentDrawnMarkerStart.y,
w: 250*this.currentScale,
h: 250*this.currentScale,
isSelected: false,
isHovered: false
};
this.addMarker(m);
}
} else if (!this.isDragging && this.isZoomed) {
this.dragStart = {
x: evt.offsetX,
y: evt.offsetY
};
this.isDragging = true;
}
}
drawRectBorder = (x0: number, y0: number, w: number, h: number, color: string, borderW: number = 1) => {
this.editContext.strokeStyle = color;
this.editContext.lineWidth = borderW;
this.editContext.beginPath();
this.editContext.rect(x0, y0, w, h);
this.editContext.stroke();
this.editContext.closePath();
}
addMarker = (r: IMarker) => {
console.log(this.currentScale)
const original = editorUtil.transformMarkerToOriginal(editorUtil.getMarkerTopLeft(r),
this.imageTopLeft, this.currentScale);
// Save coordinates on canvas
this.currentMarkers.push(original);
console.log(original)
}
/** Get the marker's top left corner, with absolute values for w,h */
export const getMarkerTopLeft = (m: IMarker): IMarker => {
const res: IMarker = {
x: m.x + (m.w < 0 ? m.w : 0),
y: m.y + (m.h < 0 ? m.h : 0),
w: Math.abs(m.w),
h: Math.abs(m.h),
isSelected: m.isSelected,
isHovered: m.isHovered
};
return res;
};
/** Get the marker's coordinates with regards to the original image dimensions */
export const transformMarkerToOriginal = (m: IMarker, imageTopLeft: Coordinate, scale: number): IMarker => {
const res: IMarker = {
x: Math.floor((m.x - imageTopLeft.x) / scale),
y: Math.floor((m.y - imageTopLeft.y) / scale),
w: Math.floor(Math.abs(m.w / scale)),
h: Math.floor(Math.abs(m.h / scale)),
isSelected: false,
isHovered: false
};
return res;
};
Thank's!!

unlimited stack of tile(object/sprite) in que after dragging the first to toppest one in phaser

i am making a game using phaser and i want to make unlimited no of tiles can think like stacked tiles. When the user drags the topmost tile and places it in the drop area, the same tile below it should appear and should be draggable.
My present code:- `
var greenTile = this.add.sprite(250,500,'greenTile');
greenTile.inputEnabled=true;
greenTile.input.enableDrag();
greenTile.events.onDragStart.add(onDragStart, this);
greenTile.events.onDragStop.add(onDragStop, this);`
the total code is of my game state is :-
`
var patternsRatio = patternsRatio || {};
var x = 50;
var y = 100;
var canvas;
var canvasBG;
var canvasGrid;
var canvasSprite;
var canvasZoom = 32;
var spriteWidth = 8;
var spriteHeight = 8;
patternsRatio.game1 = function () {};
patternsRatio.game1.prototype = {
preload: function () {
this.load.image('greenTile','assets/images/greentile.png')
this.load.image('redTile','assets/images/redtile.png')
},
create: function () {
this.game.stage.setBackgroundColor(0x2d2d2d);
var question_l1 = "Puzzle 1: Enlarge this pattern by a factor of 2."
var style_l1 = {font: "25px Comic Sans MS Bold", fill: "#fff", align:"center"};
var quet_l1=this.game.add.text(this.game.world.centerX,this.game.height-600, question_l1, style_l1)
quet_l1.anchor.setTo(0.5)
this.placetiles()
var greenTile = this.add.sprite(250,500,'greenTile');
greenTile.inputEnabled=true;
greenTile.input.enableDrag();
greenTile.events.onDragStart.add(onDragStart, this);
greenTile.events.onDragStop.add(onDragStop, this);
var redTile = this.add.sprite(100,500,'redTile');
redTile.inputEnabled=true;
redTile.input.enableDrag();
redTile.events.onDragStart.add(onDragStart, this);
redTile.events.onDragStop.add(onDragStop, this);
},
update: function () {
},
placetiles: function () {
this.game.create.grid('drawingGrid', 16 * canvasZoom, 16 * canvasZoom, canvasZoom, canvasZoom, 'rgba(0,191,243,0.8)');
canvas = this.game.make.bitmapData(spriteWidth * canvasZoom, spriteHeight * canvasZoom);
canvasBG = this.game.make.bitmapData(canvas.width + 2, canvas.height + 2);
canvasBG.rect(0, 0, canvasBG.width, canvasBG.height, '#fff');
canvasBG.rect(1, 1, canvasBG.width - 2, canvasBG.height - 2, '#3f5c67');
var x = 80;
var y = 64;
canvasBG.addToWorld(x, y);
canvasSprite = canvas.addToWorld( x + 1, y + 1); canvasZoom,canvasZoom
canvasGrid = this.game.add.sprite(x + 1, y + 1, 'drawingGrid');
canvasGrid.crop(new Phaser.Rectangle(0, 0, spriteWidth * canvasZoom, spriteHeight * canvasZoom));
}
};
`
I am also facing problem when i am adding a input.enable code below the "greenTile" variable then the redTile vanishes of can any tell why this happens.
In the image below as we can see both red and green tile are appearing.
In this image as we can i had added input enable and the red Tile stopped appearing, Secondly as have dragged the green tile to the grid, i want on the original place their should be again the greenTile should come.(this is in reference to the stack able unlimited tile question)
I could figure this out by just adding a function and calling it on the drag start event as below:-
The function that i added is :-
addGreenTiles: function(){
this.greenTile1 = this.add.sprite(250,500,'greenTile');
this.greenTile1.inputEnabled=true;
this.greenTile1.input.enableSnap(canvasZoom, canvasZoom, false, true);
this.greenTile1.input.enableDrag();
this.greenTile1.originalPosition = this.greenTile1.position.clone();
this.greenTile1.events.onDragStart.add(this.addGreenTiles, this);
this.greenTile1.events.onDragStop.add(function(currentSprite){
this.stopDrag(currentSprite, this.greenTile1);
}, this);
Then i called this function at :
this.greenTile1.events.onDragStart.add(this.addGreenTiles, this);

Pixijs attach element to another

I'm developing a tool to add various sprites to the stage. When I drag an element I'd like to display o bounding box ( a rectangle ) that need to move accordingly to the dragging item.
To handle the drag functionality I'm using a lib called draggable
This is the constructor of every single object I push on the stage:
function createElement(x, y, ass_id)
{
// create our little bunny friend..
bunny = new PIXI.Sprite(textures[ass_id]);
bunny.scale.x = bunny.scale.y = 0.2;
bunny.draggable({
snap: true,
snapTolerance:0,
grid: [ 50, 50 ],
alpha: 0.5,
mousedown: function(data) {
/*var fishBounds = new PIXI.Rectangle(
-fishBoundsPadding,
-fishBoundsPadding,
viewWidth + fishBoundsPadding * 2,
viewHeight + fishBoundsPadding * 2);*/
texture_w = (data.target.texture.width) * data.target.scale.x;
texture_h = (data.target.texture.height) * data.target.scale.y;
// scale = data.target.scale.x;
var box = new PIXI.Graphics();
box.lineStyle(2, 0x666666);
box.drawRect(data.target.position.x, data.target.position.y, texture_w, texture_h);
box.scale.x = box.scale.y = scale;
stage.addChild(box);
data.target.boundingBox = box;
console.log(data.target.boundingBox.position, data.target.position);
},
drag: function(data) {
offset_x = data.boundingBox.position.x;//data.position;
offset_y = data.boundingBox.position.y;
data.boundingBox.position.x = data.position.x;// * data.scale.x;// - offset_x;
data.boundingBox.position.y = data.position.y;// * data.scale.y;// - offset_y;
console.log(stage.children.length , data.boundingBox.position, data.position, data);
},
mouseup: function(data) {
console.log("drop");
stage.removeChild(data.target.boundingBox);
}
});
// move the sprite to its designated position
bunny.position.x = x;
bunny.position.y = y;
elements.push(bunny);
// add it to the stage
stage.addChild(elements[elements.length-1]);
}
now, this works like a charm: when I click the element a bounding box gets created in the correct location, the problem is that when I start drag it around the bounding box get away from the item. I thing that the reason for this might be due to the fact the one item is scaled, wether the other isn't, but since I'm a noob at pixi I really find myself stuck with it.
Ok, I discovered that you can easily and conveniently attach an object to another via the addChild, so it comes out like this:
function createElement(x, y, ass_id)
{
// create our little bunny friend..
bunny = new PIXI.Sprite(textures[ass_id]);
bunny.scale.x = bunny.scale.y = 0.2;
bunny.draggable({
snap: true,
snapTolerance:0,
grid: [ 50, 50 ],
alpha: 0.5,
mousedown: function(data) {
texture_w = (data.target.texture.width);
texture_h = (data.target.texture.height);
var box = new PIXI.Graphics();
box.lineStyle(5, 0x666666);
box.drawRect(0, 0, texture_w, texture_h);
data.target.type = "element";
data.target.addChild(box);
},
drag: function(data) {
},
mouseup: function(data) {
console.log("drop");
for (var i = stage.children.length - 1; i >= 0; i--) {
if((stage.children[i].type) && (stage.children[i].type == "element"))
for (var j = stage.children[i].length - 1; i >= 0; i--) {
console.log('remove boundingBox child here when needed');
}
};
}
});
// move the sprite to its designated position
bunny.position.x = x;
bunny.position.y = y;
elements.push(bunny);
// add it to the stage
stage.addChild(elements[elements.length-1]);
}

drawImage with Canvas is sending image to the back of the canvas [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
I have been doing a little experimenting with canvas, creating drawings using lines shapes text etc, and inserting .png files. The inserting .png files is the bit that I cannot get to work.
Edit:
Undesired behaviour of this code: I load shapes to the graphics context, then load an image file to the graphics context, however when the graphics context is drawn, the image is at behind the shapes, despite being drawn last.
I wanted the image file to be at the top, in front of the shapes.
Desired behaviour: To bring image file to the front of the canvas, so it is not hidden by shapes drawn in the graphics context.
function loadImage(name) {
images[name] = new Image();
images[name].src = "DogWalking/" + name + ".png";
images[name].onload = function() {
graphics.drawImage(this, 0, 300);
canvas.bringToFront(this);
};
}
the function for drawing is called here:
function draw() {
graphics.save(); // to make sure changes don't carry over from one call to the next
graphics.fillStyle = "transparent"; // background color
graphics.fillRect(0,0,wWidth, wHeight);
graphics.fillStyle = "black";
applyLimits(graphics,xleft,xright,ytop,ybottom,true);
graphics.lineWidth = pixelSize;
world.draw(graphics);
graphics.drawImage(images["dog-walking11"],200,200);
graphics.restore();
}
code for the whole page is
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<head>
<title>Hierarchical Modeling 2D</title>
<style>
#messagediv {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 0;
background-color: indigo;
}
#canvasdiv {
position: absolute;
left: 0;
top: 0;
z-index: 10;
background-color: transparent;
}
</style>
<script type="text/javascript" src="rgbcolor.js"></script>
<script type="text/javascript">
"use strict";
var totalResources = 17;
var numResourcesLoaded = 0;
var images = {};
function loadImage(name) {
images[name] = new Image();
images[name].src = "DogWalking/" + name + ".png";
images[name].onload = function() {
//
graphics.drawImage(this, 0, 300);
canvas.bringToFront(this);
}
}
var canvas; // DOM object corresponding to the canvas
var graphics; // 2D graphics context for drawing on the canvas
var ctx; // 2D graphics context for drawing on the canvas
var myNumber = 0, myNumber2 = 0, myInterval, myInterval2, myelement, thisdiv, printx;
var mycoords = new Array();
var pcoords = new Array(); //coordinates of the portal.
//var pcoords = [[0,0], [50,300], [250,150]]; //coordinates of the portal.
var nocoords = 2;
var frameNumber = 0; // Current frame number.
var frameNumber2 = 0;
var sun;
var sun2;
var ground;
var world;
var pixelSize;
var wWidth;
var wHeight;
var portals = new Array("calendar1","alternativsearch","art1", "directory1");
var portalsval = new Array();
var portalsobj;
var leftj = new Array(3,1,4,2);
var forwards = "http://www.alternativworld.com";
// ---------------- Set Page Layout ----------------
// function to set size of canvas and location of portals
function pageLayout() {
var w = window, d = document, e = d.documentElement, g = d.getElementsByTagName('body')[0];
wWidth = w.innerWidth || e.clientWidth || g.clientWidth;
wHeight = w.innerHeight|| e.clientHeight|| g.clientHeight;
// Adjust wWidth and wHeight if ratio does not match scenary 7 by 5.
if (wWidth/wHeight != 7/5)
if (wWidth/wHeight > 7/5) {
var widthPortion = 5 * wWidth/wHeight;
wWidth = wWidth * 7 / widthPortion;
} else {
var heightPortion = 7 * wHeight/wWidth;
wHeight = wHeight * 5 / heightPortion;
}
var widthheight, localerror = false;
widthheight = Math.min(wWidth, wHeight);
if(widthheight < 400){
var localerror = true;
}
if (localerror == true)
alert("Warning, the page size of your browser or your screen resolution may be too small to correctly view this web page.");
var theCanvas = d.getElementById("theCanvas");
theCanvas.height = wHeight;
theCanvas.width = wWidth;
}
//Function to listen to the mouse events and see if a link is selected.
function doMouseDown(evt) {
var r = canvas.getBoundingClientRect();
var x = Math.round(evt.clientX - r.left);
var y = Math.round(evt.clientY - r.top);
alert(evt.clientX+ " " + evt.clientY);
for (var i = portals.length+1; i >= 0; i--) {
var p = pcoords[i];
if (Math.abs(p[0] - x) <= 50 && Math.abs(p[1] - y) <= 50) {
document.location.href = forwards;
return;
} else if (Math.abs(0 - x) <= 50 && Math.abs(0 - y) <= 50){
document.location.href = "http://www.alternativeuk.co.uk";
return;
}
}
}
// ---------------- The object-oriented scene graph API ------------------
/**
* The base class for all nodes in the scene graph data structure.
*/
function SceneGraphNode() {
this.fillColor = null; // If non-null, the default fillStyle for this node.
this.strokeColor = null; // If non-null, the default strokeStyle for this node.
}
SceneGraphNode.prototype.doDraw = function(g) {
// This method is meant to be abstract and must be
// OVERRIDDEN in any actual object in the scene graph.
// It is not meant to be called; it is called by draw().
throw "doDraw not implemented in SceneGraphNode"
}
SceneGraphNode.prototype.draw = function(g) {
// This method should be CALLED to draw the object
// represented by this SceneGraphNode. It should NOT
// ordinarily be overridden in subclasses.
graphics.save();
if (this.fillColor) {
g.fillStyle = this.fillColor;
}
if (this.strokeColor) {
g.strokeStyle = this.strokeColor;
}
this.doDraw(g);
graphics.restore();
}
SceneGraphNode.prototype.setFillColor = function(color) {
// Sets fillColor for this node to color.
// Color should be a legal CSS color string, or null.
this.fillColor = color;
return this;
}
SceneGraphNode.prototype.setStrokeColor = function(color) {
// Sets strokeColor for this node to color.
// Color should be a legal CSS color string, or null.
this.strokeColor = color;
return this;
}
SceneGraphNode.prototype.setColor = function(color) {
// Sets both the fillColor and strokeColor to color.
// Color should be a legal CSS color string, or null.
this.fillColor = color;
this.strokeColor = color;
return this;
}
/**
* Defines a subclass, CompoundObject, of SceneGraphNode to represent
* an object that is made up of sub-objects. Initially, there are no
* sub-objects.
*/
function CompoundObject() {
SceneGraphNode.call(this); // do superclass initialization
this.subobjects = []; // the list of sub-objects of this object
}
CompoundObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
CompoundObject.prototype.add = function(node) {
// Add node a subobject of this object. Note that the
// return value is a reference to this node, to allow chaining
// of method calls.
this.subobjects.push(node);
return this;
}
CompoundObject.prototype.doDraw = function(g) {
// Just call the sub-objects' draw() methods.
for (var i = 0; i < this.subobjects.length; i++)
this.subobjects[i].draw(g);
}
/**
* Define a subclass, TransformedObject, of SceneGraphNode that
* represents an object along with a modeling transformation to
* be applied to that object. The object must be specified in
* the constructor. The transformation is specified by calling
* the setScale(), setRotate() and setTranslate() methods. Note that
* each of these methods returns a reference to the TransformedObject
* as its return value, to allow for chaining of method calls.
* The modeling transformations are always applied to the object
* in the order scale, then rotate, then translate.
*/
function TransformedObject(object) {
SceneGraphNode.call(this); // do superclass initialization
this.object = object;
this.rotationInDegrees = 0;
this.scaleX = 1;
this.scaleY = 1;
this.translateX = 0;
this.translateY = 0;
}
TransformedObject.prototype = new SceneGraphNode(); // (makes it a subclass!)
TransformedObject.prototype.setRotation = function(angle) {
// Set the angle of rotation, measured in DEGREES. The rotation
// is always about the origin.
this.rotationInDegrees = angle;
return this;
}
TransformedObject.prototype.setScale = function(sx, sy) {
// Sets scaling factors.
this.scaleX = sx;
this.scaleY = sy;
return this;
}
TransformedObject.prototype.setTranslation = function(dx,dy) {
// Set translation mounts.
this.translateX = dx;
this.translateY = dy;
return this;
}
TransformedObject.prototype.doDraw = function(g) {
// Draws the object, with its modeling transformation.
g.save();
if (this.translateX != 0 || this.translateY != 0) {
g.translate(this.translateX, this.translateY);
}
if (this.rotationInDegrees != 0) {
g.rotate(this.rotationInDegrees/180*Math.PI);
}
if (this.scaleX != 1 || this.scaleY != 1) {
g.scale(this.scaleX, this.scaleY);
}
this.object.draw(g);
g.restore();
}
/**
* A subclass of SceneGraphNode representing filled triangles.
* The constructor specifies the vertices of the triangle:
* (x1,y1), (x2,y2), and (x3,y3).
*/
function Triangle(x1,y1,x2,y2,x3,y3) {
SceneGraphNode.call(this);
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.x3 = x3;
this.y3 = y3;
}
Triangle.prototype = new SceneGraphNode();
Triangle.prototype.doDraw = function(g) {
g.beginPath();
g.moveTo(this.x1,this.y1);
g.lineTo(this.x2,this.y2);
g.lineTo(this.x3,this.y3);
g.closePath();
g.fill();
}
/**
* Directly create a line object as a SceneGraphNode with a
* custom doDraw() method. line is of length 1 and
* extends along the x-axis from (0,0) to (1,0).
*/
var line = new SceneGraphNode();
line.doDraw = function(g) {
g.beginPath();
g.moveTo(0,0);
g.lineTo(1,0);
g.stroke();
}
/**
* Directly create a filled rectangle object as a SceneGraphNode with a
* custom doDraw() method. filledRect is a square with side 1, centered
* at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5).
*/
var filledRect = new SceneGraphNode();
filledRect.doDraw = function(g) {
g.fillRect(-0.5,-0.5,1,1);
}
/**
* Directly create a rectangle object as a SceneGraphNode with a
* custom doDraw() method. rect is a square with side 1, centered
* at (0,0), with corners at (-0.5,-0.5) and (0.5,0.5). Only the
* outline of the square is drawn.
*/
var rect = new SceneGraphNode();
rect.doDraw = function(g) {
g.strokeRect(-0.5,-0.5,1,1);
}
/**
* Directly create a filled circle object as a SceneGraphNode with a
* custom doDraw() method. filledCircle is a circle with radius 0.5
* (diameter 1), centered at (0,0).
*/
var filledCircle = new SceneGraphNode();
filledCircle.doDraw = function(g) {
g.beginPath();
g.arc(0,0,0.5,0,2*Math.PI);
g.fill();
}
var clickHere = new SceneGraphNode();
clickHere.doDraw = function(g) {
g.fillText("click here :)",0,0)
}
/**
* Directly create a circle object as a SceneGraphNode with a
* custom doDraw() method. filledCircle is a circle with radius 0.5
* (diameter 1), centered at (0,0). Only the outline of the circle
* is drawn.
*/
var circle = new SceneGraphNode();
circle.doDraw = function(g) {
g.beginPath();
g.arc(0,0,0.5,0,2*Math.PI);
g.stroke();
}
var dog = new SceneGraphNode();
dog.doDraw = function(g) {
g.drawImage(images["dog-walking11"],-2, 2);
alert(images["dog-walking11"].name);
}
// -------------------- Specific to this application ----------------------------
/*
* Define two extra basic objects as SceneGraphNodes with custom doDraw() methods.
* One represents the ground, the other a vane for a windmill.
*/
var ground = new SceneGraphNode();
ground.doDraw = function(g) {
g.beginPath();
g.moveTo(0,-1);
g.lineTo(0,0.8);
g.lineTo(1.5,1.65);
g.lineTo(1.8,1.3);
g.lineTo(3,2.1);
g.lineTo(4.7,0.7);
g.lineTo(6.1,1.2);
g.lineTo(7,0.8);
g.lineTo(7,-1);
g.closePath();
g.fill();
}
var windmillVane = new SceneGraphNode();
windmillVane.doDraw = function(g) {
g.beginPath();
g.moveTo(0,0);
g.lineTo(0.5,0.1);
g.lineTo(1.5,0);
g.lineTo(0.5,-0.1);
g.closePath();
g.fill();
}
var world; // A SceneGraphNode representing the entire picture. This should
// be created in the createWorld() method.
var pixelSize; // The size of one pixel, in the transformed coordinates.
// This is used as the default width of a stroke.
var background = "#C8C8FF"; // A CSS color string giving the background color.
// the draw() function fills the canvas with this color.
var xleft = 0; // The requested xy-limits on the canvas, after the
var xright = 7; // coordinate transformation has been applied.
var ybottom = -1; // The transformation is applied in the draw() function.
var ytop = 4;
var frameNumber = 0; // Current frame number.
var cart; // TransformedObjects that are animated.
var wheel;
var sun;
var clickText1;
var clickText2;
var rotor;
/**
* Create the scene graph data structure. The global variable world must
* refer to the root node of the scene graph. This function is called in
* the init() function.
*/
function createWorld() {
pageLayout();
var i;
var sunTemp = new CompoundObject();
sunTemp.setColor("yellow"); // color for filled circle and light rays
for (i = 0; i < 12; i++) { // add the 12 light rays, with different rotations
sunTemp.add( new TransformedObject(line).setScale(0.75,0.75).setRotation(i*30) );
}
sunTemp.add( filledCircle ); // the face of the sun
sunTemp.add( new TransformedObject(circle).setColor("#B40000") ); // outlines the face
sun = new TransformedObject(sunTemp);
clickText1 = new TransformedObject(clickHere).setColor("#B40000").setScale(0.01,-0.01);
var wheelTemp = new CompoundObject();
wheelTemp.setColor("black"); // color for all but one of the subobjects
wheelTemp.add( new TransformedObject(filledCircle).setScale(2,2) );
wheelTemp.add( new TransformedObject(filledCircle).setScale(1.6,1.6).setColor("#CCCCCC") );
wheelTemp.add( new TransformedObject(filledCircle).setScale(0.4,0.4) );
for (i = 0; i < 12; i++) { // add the 12 spokes
wheelTemp.add( new TransformedObject(line).setRotation(i*30) );
}
wheel = new TransformedObject(wheelTemp);
var cartTemp = new CompoundObject();
cartTemp.setColor("red"); // color for the rectangular body of the cart
cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(1.5,-0.1) );
cartTemp.add( new TransformedObject(wheel).setScale(0.8,0.8).setTranslation(-1.5,-0.1) );
cartTemp.add( new TransformedObject(filledRect).setScale(5,2).setTranslation(0,1) ); // the body of the cart
cart = new TransformedObject(cartTemp).setScale(0.3,0.3);
clickText2 = new TransformedObject(clickHere).setColor("yellow").setScale(0.01,-0.01);
var rotorTemp = new CompoundObject(); // a "rotor" consisting of three vanes
rotorTemp.setColor( "#C86464" ); // color for all of the vanes
rotorTemp.add( windmillVane );
rotorTemp.add( new TransformedObject(windmillVane).setRotation(120) );
rotorTemp.add( new TransformedObject(windmillVane).setRotation(240) );
rotor = new TransformedObject(rotorTemp);
var windmill = new CompoundObject();
windmill.setColor("#E0C8C8"); // color for the pole
windmill.add( new TransformedObject(filledRect).setScale(0.1,3).setTranslation(0,1.5) ); // the pole
windmill.add( new TransformedObject(rotor).setTranslation(0,3) ); // the rotating vanes
world = new CompoundObject();
world.setColor("#00961E"); // color used for the ground only
world.add(ground);
//world.add( new TransformedObject(filledRect).setScale(7,0.8).setTranslation(3.5,0).setColor("#646496") ); // road
//world.add( new TransformedObject(filledRect).setScale(7,0.06).setTranslation(3.5,0).setColor("white") ); // line in road
world.add( new TransformedObject(windmill).setScale(0.6,0.6).setTranslation(0.75,1) );
world.add( new TransformedObject(windmill).setScale(0.4,0.4).setTranslation(2.2,1.3) );
world.add( new TransformedObject(windmill).setScale(0.7,0.7).setTranslation(3.7,0.8) );
world.add( new TransformedObject(sun).setTranslation(5.5,3.3) );
world.add( new TransformedObject(clickText1).setTranslation(5.25,3.3) );
world.add( cart );
world.add( clickText2 );
//alert(2);
}
/**
* This will be called before each frame is drawn.
*/
function updateFrame() {
frameNumber++;
if (frameNumber>= 312){
frameNumber = 0;
frameNumber2 = 1;
}
cart.setTranslation(-3 + 13*(frameNumber % 300) / 300.0, 0);
clickText2.setTranslation(-3.3 + 13*(frameNumber % 300) / 300.0, 0.25);
if (typeof(pcoords[5]) != 'undefined') {
pcoords[5][0] = (-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*canvas.width / (xright-xleft);
pcoords[5][1] = (0.25-ytop)*canvas.height / (ybottom-ytop);
}
wheel.setRotation(-frameNumber*3.1);
sun.setRotation(-frameNumber);
rotor.setRotation(frameNumber * 2.7);
}
// ------------------------------- graphics support functions --------------------------
/**
* Draw one frame of the animation. Probably doesn't need to be changed,
* except maybe to change the setting of preserveAspect in applyLimits().
*/
function draw() {
graphics.save(); // to make sure changes don't carry over from one call to the next
graphics.fillStyle = "transparent"; // background color
graphics.fillRect(0,0,wWidth, wHeight);
graphics.fillStyle = "black";
applyLimits(graphics,xleft,xright,ytop,ybottom,true);
graphics.lineWidth = pixelSize;
world.draw(graphics);
graphics.drawImage(images["dog-walking11"],200,200);
graphics.restore();
}
/**
* Applies a coordinate transformation to the graphics context, to map
* xleft,xright,ytop,ybottom to the edges of the canvas. This is called
* by draw(). This does not need to be changed.
*/
//pcoords[0][0] =
//pcoords[0][1]=
function applyLimits(g, xleft, xright, ytop, ybottom, preserveAspect) {
var width = canvas.width; // The width of this drawing area, in pixels.
var height = canvas.height; // The height of this drawing area, in pixels.
var k = portals.length;
var j;
var i = 0, widthheight, myradius;
var localerror = false;
if (pcoords.length < k) {
while (portals[i]){
j = i + 1;
if (width > 100){
var rWidth = width/(k + 1);
rWidth= Math.floor(rWidth);
} else {
var lWidth = 0;
var rWidth = 0;
}
if (height > 100){
var bHeight = height/(k + 1);
bHeight= Math.floor(bHeight);
} else {
var tHeight = 0;
var bHeight = 0;
}
var myleft = leftj[i] * rWidth - 50;
var mytop = j * bHeight - 50;
pcoords[i]= new Array;
pcoords[i][0] = myleft;
pcoords[i][1] = mytop;
i = i + 1;
}
}
if (preserveAspect) {
// Adjust the limits to match the aspect ratio of the drawing area.
var displayAspect = Math.abs(height / width);
var requestedAspect = Math.abs(( ybottom-ytop ) / ( xright-xleft ));
var excess;
if (displayAspect > requestedAspect) {
excess = (ybottom-ytop) * (displayAspect/requestedAspect - 1);
ybottom += excess/2;
ytop -= excess/2;
}
else if (displayAspect < requestedAspect) {
excess = (xright-xleft) * (requestedAspect/displayAspect - 1);
xright += excess/2;
xleft -= excess/2;
}
}
var pixelWidth = Math.abs(( xright - xleft ) / width);
var pixelHeight = Math.abs(( ybottom - ytop ) / height);
pixelSize = Math.min(pixelWidth,pixelHeight);
if (frameNumber == 4 || frameNumber == 5){
pcoords.push([(5.25-xleft)*width / (xright-xleft),(3.3-ytop)*height / (ybottom-ytop)]);
pcoords.push([(-3.3 + 13*(frameNumber % 300) / 300.0-xleft)*width / (xright-xleft), (0.25-ytop)*height / (ybottom-ytop)]);
}
g.scale( width / (xright-xleft), height / (ybottom-ytop) );
g.translate( -xleft, -ytop );
// if (frameNumber < 3)
}
//------------------ Animation framework ------------------------------
var animationTimeout = null; // A null value means the animation is off.
// Otherwise, this is the timeout ID.
function frame() {
// Draw one frame of the animation, and schedule the next frame.
updateFrame();
draw();
canvas.addEventListener("mousedown", doMouseDown, false);
animationTimeout = setTimeout(frame, 33);
}
function setAnimationRunning(run) {
if ( run ) {
if (animationTimeout == null) {
// If the animation is not already running, start
// it by scheduling a call to frame().
animationTimeout = setTimeout(frame, 33);
}
}
else {
if (animationTimeout != null) {
// If the animation is running, stop it by
// canceling the next scheduled call to frame().
clearTimeout(animationTimeout);
}
animationTimeout = null; // Indicates that animation is off.
}
}
//----------------------- initialization -------------------------------
function init() {
try {
canvas = document.getElementById("theCanvas");
if(typeof G_vmlCanvasManager != 'undefined') {
canvas = G_vmlCanvasManager.initElement(canvas);
}
graphics = canvas.getContext("2d");
}
catch (e) {
document.getElementById("message").innerHTML =
"Sorry, this page requires canvas graphics, but<br>" +
"it looks like your browser does not support it<br>" +
"Reported error: " + e;
return;
}
// add any other necessary initialization
document.getElementById("animateCheck").checked = true; // Make sure box is checked!
loadImage("dog-walking11");
createWorld();
setAnimationRunning(true); // start the animation
}
</script>
</head>
<body onload="init()" style="background-color: rgb(220,220,220)">
<div id="messagediv">
<h2>Hierarchical Modeling Example</h2>
<!-- For error reporting: the contents of the noscript tag are
shown only if JavaScript is not available. The paragraph with
id="message" is for reporting errors using JavaScript.-->
<noscript><b>This page requires JavaScript, which is<br>
not enabled in your browser.</b></noscript>
<p id="message" style="color:red"></p>
<p><input type="checkbox" id="animateCheck" onchange="setAnimationRunning(this.checked)">
<label for="animateCheck">Run Animation</label>
</p>
</div>
<div id="canvasdiv">
<canvas id="theCanvas" width= "400" height= "300"
style="background-color: transparent"></canvas>
</div>
</body>
</html>
It seems that I had to use graphics.drawImage() after using graphics.restore().
Though I was trying to draw the image in the correct order (after), compared to drawing the rectangles, circles etc, the shapes did not come out of the buffer onto the page until after the restore function().
When I was calling drawImage, I assumed that it was loading it onto the buffered canvas with the rest of the things, ready to be drawn in the correct order, when in fact it was putting it straight onto the page.
It seems strange since it was being called with the graphics context, so I thought it would be added to the rest of the canvas graphics, but i appear to be wrong.

Categories