I know there's a similar question here, but neither the question nor the answer have any code.
What I want to do, is to port this functionality to a 100% Javascript solution. Right now I'm able to draw a rectangle on top of HTML content using PHP.
I scrape a website with CasperJS, take a snapshot, send the snapshot path back to PHP along with a json object that contains all the information necessary to draw a rectangle with GD libraries. That works, but now I want to port that functionality into Javascript.
The way I'm getting the rectangle information is using getBoundingClientRect() that returns an object with top,bottom,height, width, left,and right.
Is there any way to "draw" the HTML of the website to a canvas element, and then draw a Rectangle on that canvas element?
Or is there any way to draw a rectangle on top of the HTML using Javascript?
Hope my question is clear enough.
This is the function that I'm using to get the coordinates of the elements that I want to draw a Rectangle around.
function getListingRectangles() {
return Array.prototype.map.call(document.querySelectorAll('.box'), function(e) {
return e.getBoundingClientRect();
});
You can create a canvas element and place it on top of the HTML page:
//Position parameters used for drawing the rectangle
var x = 100;
var y = 150;
var width = 200;
var height = 150;
var canvas = document.createElement('canvas'); //Create a canvas element
//Set canvas width/height
canvas.style.width='100%';
canvas.style.height='100%';
//Set canvas drawing area width/height
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
//Position canvas
canvas.style.position='absolute';
canvas.style.left=0;
canvas.style.top=0;
canvas.style.zIndex=100000;
canvas.style.pointerEvents='none'; //Make sure you can click 'through' the canvas
document.body.appendChild(canvas); //Append canvas to body element
var context = canvas.getContext('2d');
//Draw rectangle
context.rect(x, y, width, height);
context.fillStyle = 'yellow';
context.fill();
This code should add a yellow rectangle at [100, 150] pixels from the top left corner of the page.
Related
I am creating a web-based annotation application for annotating images via the HTML canvas element and Javascript. I would like the user to mouse down to indicate the start of the rectangle, drag to the desired end coordinate and let go to indicate the opposite end of the rectangle.
Currently, I am able to take the starting coordinates and end coordinates to create a rectangle on the image with the context.rects() function, however as I am uncertain on how to resize a specific rectangle on the canvas, that leaves me with the rectangle only being drawn after the user has released the mouse click.
How would I be able to resize a specific rectangle created onmousedown while dragging?
The following is the code snippet that performs the function:
var isMouseDown = false;
var startX;
var startY;
canvas.onmousedown = function(e) {
if(annMode){
isMouseDown = true;
var offset = $(this).offset();
startX = parseInt(e.pageX - offset.left);
startY = parseInt(e.pageY - offset.top);
}
};
canvas.onmousemove = function(e) {
if(isMouseDown) {
var offset = $(this).offset();
var intermediateX = parseInt(e.pageX - offset.left);
var intermediateY = parseInt(e.pageY - offset.top);
console.log(intermediateX);
}
};
canvas.onmouseup = function(e) {
if(annMode&&isMouseDown){
isMouseDown = true;
var offset = $(this).offset();
var endX = parseInt(e.pageX - offset.left);
var endY = parseInt(e.pageY - offset.top);
var width = endX - startX;
var height = endY - startY;
context.strokeStyle = "#FF0000";
context.rect(startX, startY, width, height);
context.stroke();
}
isMouseDown = false
};
Here my handy-front-end scripts come in handy!
As I understood the question, you wanted to be able to move your mouse to any point on the canvas, hold the left mouse button, and drag in any direction to make a rectangle between the starting point and any new mouse position. And when you release the mouse button it will stay.
Scripts that will help you accomplish what you are trying to do:
https://github.com/GustavGenberg/handy-front-end/blob/master/README.md#canvasjs
https://github.com/GustavGenberg/handy-front-end/blob/master/README.md#pointerjs
Both scripts just makes the code a lot cleaner and easier to understand, so I used those.
Here is a fiddle as simple as you can make it really using
const canvas = new Canvas([]);
and
const mouse = new Pointer();
https://jsfiddle.net/0y8cbao3/
Did I understand your question correctly?
Do you want a version with comments describing every line and what is does?
There are still some bugs at the moment but im going to fix those soon!
EDIT
After reading your questions again, I reacted to: "...however as I am uncertain on how to resize a specific rectangle on the canvas...".
Canvas is like an image. Once you have drawn to it, you can NOT "resize" different shapes. You can only clear the whole canvas and start over (ofcourse you can clear small portions too).
That's why the Canvas helper is so helpful. To be able to "animate" the canvas, you have to create a loop that redraws the canvas with a new frame each 16ms (60 fps).
The canvas API does not preserve references to specific shapes drawn with it (unlike SVG). The canvas API simply provides convenient functions to apply operations to the individual pixels of the canvas element.
You have a couple options to achieve a draggable rectangle:
You can position a styled div over your canvas while the user is dragging. Create a container for your canvas and the div, and update the position and size the div. When the user releases, draw your rectangle. Your container needs to have position: relative and the div needs to be absolutely positioned. Ensure the div has a higher z-index than the canvas.
In your mouse down method, set div.style.display to block. Then update the position (style.left, style.top, style.width, and style.height) as the mouse is dragged. When the mouse is released, hide it again (style.display = 'none').
You can manually store references to each item you want to draw, clear the canvas (context.clearRect), and redraw each item on the canvas each frame. This kind of setup is usually achieved through recursive usage of the window.requestAnimationFrame method. This method takes a callback and executes on the next draw cycle of the browser.
The first option is probably easier to achieve in your case. If you plan to expand the capabilities of your app further, the 2nd will provide more versatility. A basic loop would be implemented as so:
// setup code, create canvas & context
function mainLoop() {
context.clearRect(0, 0, canvas.width, canvas.height);
/** do your logic here and re-draw **/
requestAnimationFrame(mainLoop);
}
function startApp() {
requestAnimationFrame(mainLoop)
}
This tutorial has detailed explanation of event loops for HTML canvas: http://www.isaacsukin.com/news/2015/01/detailed-explanation-javascript-game-loops-and-timing
I also have a fully featured implementation on my GitHub that's part of rendering engine I wrote: https://github.com/thunder033/mallet/blob/master/src/mallet/webgl/webgl-app.ts#L115
I want to set background image to my rectangle in the canvas.
So far i've done this.
var img = new Image()
img.src = //Source of an image.
var newPattern = ctx.createPattern(img, "no-repeat");
ctx.fillStyle = newPattern;
And it works, but the problem is, that it applies the image to the canvas, not to the rectangle.
And whenever I change the position of the rectangle, the image remains in the same position.
Can anybody suggest how to fix this, so the image'd be attached to the rectangle.
If you want to only draw the image within a specified rectangle you can do something like this.
context.rect(x, y, width, height);
context.clip();
context.drawImage(img, 0, 0);
This creates a rectangle at (x, y) with size (width, height) which is used for all future calls to the context. If you want to stop the clipping you will need to add a
context.save();
before the code above and then a
context.restore();
after you have drawn the image.
JSFiddle
Edit: updated to rect
I am working on a multiple web game using JavaScript. My canvas is currently set to the width and the height of my screen.
html
<canvas id = "canvas"></canvas>
javascript
var c=document.getElementById("canvas");
var ctx = c.getContext("2d");
//Making canvas scale;
c.width = window.innerWidth;
c.height = window.innerHeight;
function resize(){
//Add some code
}
My problem is, I do not want my players to zoom out, well not by default. It will make the game look bad and give the players an edge over everyone else. So I need to add some code to go into the resize method, that regardless of scale, the canvas will not be zoomed out. If the end result is something blurry at 300%+ that is fine.
IMPORTANT: the resize function cannot remove or reset the canvas back to default.
There are various ways to scale a canvas.
First off, there are 2 main parameters for the canvas size:
-Canvas Pixel Count. Set via canvas.width = 1000
-Canvas Display Pixel Size. Set via canvas.style.width = '1000px'
If you want all players to see a 1000x1000 region but displaying it fullscreen:
canvas.width = 1000;
canvas.height = 1000;
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
There is also another option with canvas.style.transform = 'scale(2,2)'.
This method is the closest thing to the browser zoom done via Ctrl+ or Ctrl-.
The big advantage of transform is that the scaling is applied to all DOM children elements. If your game is using HTML for its interface, then this is the way to go. (By applying the scaling on the div containing the canvas + HTML interface.
I am trying to make a 2d top-down game in Javascript at the moment. I've currently got a day/night system working where a black rectangle gradually becomes more opaque (as the day goes on) before it finally is fully opaque, simulating the peak of the night where the player can not see.
I want to implement an artificial light system, where the player could use a torch that will illuminate a small area in-front of them. However, my problem is that I don't seem to be able to find a way to 'cut out' a shape from my opaque rectangle. By cutting out a shape, it would look like the player has a torch.
Please find an example mock-up image I made below to show what I mean.
http://i.imgur.com/VqnTwoR.png
Obviously the shape shouldn't be as roughly drawn as that :)
Thanks for your time,
Cam
EDIT: The code used to draw the rectangle is as follows:
context.fillStyle = "#000033";
context.globalAlpha = checkLight(gameData.worldData.time);
context.fillRect(0, 0, 512, 480);
//This is where you have to add the cut out triangles for light!
context.stroke();
Instead of drawing a rectangle over the scene to darken it when the "light" is on, instead draw an image with the "lit" area completely transparent and the rest of the "dark" area more opaque.
One way is to use declare a triangular clipping area and draw your revealed scene. The scene would display only inside the defined clipping area.
Example code and a Demo:
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = 75;
var offset = 50;
var img = new Image();
img.onload = function() {
knockoutAndRefill(50,100,300,50,75,350);
};
img.src = 'http://guideimg.alibaba.com/images/trip/1/03/18/7/landscape-arch_68367.jpg';
function knockoutAndRefill(x0,y0,x1,y1,x2,y2){
context.save();
context.fillStyle='black';
context.fillRect(0,0,canvas.width,canvas.height);
context.beginPath();
context.moveTo(x0,y0);
context.lineTo(x1,y1);
context.lineTo(x2,y2);
context.closePath();
context.clip();
context.drawImage(img,0,0);
context.restore();
}
<canvas id=myCanvas width=500 height=400>
Here is my code:
$('#add_shape').click(function() {
var rectHeight = $('#rect_height_input').val();
var rectWidth = $('#width_input').val();
$('<canvas>').attr({
id: 'canvas'
}).css({
height: function() {
if (rectHeight > 0) {
return rectHeight + 'px';
}
else {
return rectWidth + 'px';
}
},
width: rectWidth + 'px'
}).appendTo('#work_area');
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = $('#color_list option:selected').val();
ctx.fillRect(0, 0, rectWidth, rectHeight);
});
When the #add_shape button is clicked, the rectangle does not show up. What am I missing here? Please help.
In response to previous revision:
You retrieved existing canvas element using document.getElementById() and then you created another one using jQuery $('<canvas>') and appended it to #work_area.
Change $('<canvas>') into $(canvas) to use the already existing canvas.
Did you really want to call document.getElementById('canvas') instead of document.createElement('canvas')? In the 1st case there must be already a canvas element in the DOM with the id="canvas" which looks suspicious.
Edit #1 (in response to current revision):
If that was just a wrote code in the answer then check your HTML (which you should also provide). Your code is working as demonstrated in this fiddle.
Make sure that IDs are correct - You are using #rect_height_input for height, but #width_input for width and double-check values for the color options - maybe the rectangle is drawin using white color.
Edit #2 (in response to the fiddle in comments):
For each shape you create a new canvas element and all those elements have same (!) id. This is incorrect. Attribute id of an element should be unique. In your code you will always get the first canvas and draw on it - now matter how many shapes you crated. Rest of canvas elements are empty.
Your code is drawing rectangles correctly (apart from the "same canvas" problem). Try to draw rectangle with big height and width - it is working. When the width/height are very small then the canvas is too small to show the rectangle. Setting minimum width/height or using one big canvas and drawing shapes onto it is a way to go.