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.
Related
I want to use HTML5 canvas with flexbox. I need to set canvas.width and canvas.height automatically when user resizes window. I have tried to use jQuery for it:
$(".cnvs").attr("width", $(".cnvs").width());
$(".cnvs").attr("height", $(".cnvs").height());
but it keeps increasing actual width of canvas so it almost fills entire screen. I have put it on jsfiddle - try to resize output window with separator.
Is there any reasonable way how to do it? Thanks.
Edit: Just to be clear: I don't want to fill entire screen with that canvas. I want UI where I have:
<div class="container">
<div class="control"></div>
<canvas></canvas>
<div class="control"></div>
</div>
then use flexbox to put those three elements beside eachother, while canvas will be twice as wide as the other. This works without problem, but canvas.width and canvas.height doesn't get updated, so whenever I render something onto that canvas, it is rendered as if that canvas was 320x140 px.
Edit 2: I am sorry, but (perhaps because of my poor English) I am not clear enough. I will try to explain it once again:
Actual width of canvas element is correct (even without using any JavaScript code) only by using flexbox. My problem is that although width is correct (and $(".cnvs").width() returns correct value of width), it doesn't have any "width" attribute, it is still:
<canvas>
</canvas>
and I need to provide width argument by myself (because it renders badly when it's not set). When I try to use my code or proposed:
...
var rect = canvas.parentNode.getBoundingClientRect();
canvas.width = rect.width;
canvas.height = rect.height;
...
it behaves weirdly, canvas's width keeps increasing with every resize, but too much (it erases both control divs almost immediately).
Update 2
If I understand the question correct: the canvas has a flex CSS set (not shown in the question right now). It defines the canvas to be 2x the size of the other two elements, but since the canvas is resized and not its bitmap, the drawings are stretches as well and you want the bitmap to adopt dynamically.
If so, do this change to the code -
This update will leave the CSS rules of the canvas element alone and let flexbox handle it. It will read the actual pixel size of the element and then apply it to the bitmap so that data isn't stretched:
$(window).on("resize", function() {
var cnvs = $(".cnvs")[0]; // cache canvas element
var rect = cnvs.getBoundingClientRect(); // actual size of canvas el. itself
cnvs.width = rect.width;
cnvs.height = rect.height;
// ... redraw content here ...
}
Additionally, since resizing the window can produce a lot of events, you may want to consider "debouncing" so that you only deal with the more recent resize:
var timerID;
$(window).on("resize", function() {
clearTimeout(timerID);
timerID = setTimeout(function() {
var cnvs = $(".cnvs")[0]; // cache canvas element
var rect = cnvs.getBoundingClientRect(); // actual size of canvas el. itself
cnvs.width = rect.width;
cnvs.height = rect.height;
// ... redraw content here ...
}, 180); // adjust at will
}
This will delay the resizing/redrawing until 180ms has passed.
You don't even have to load jQuery, it is simple:
var canvas = document.querySelector("canvas");
window.addEventListener("resize", function(){
canvas.setAttribute("width", window.innerWidth);
canvas.setAttribute("height", window.innerHeight)
})
You have to add a listener to the resize event on the window object.
If your looking for a jquery solution.
$( window ).resize(function() {
$(".cnvs").attr("width", $(window).width());
$(".cnvs").attr("height", $(window).height());
});
This should work just fine.
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.
I want to give a div, which containing a canvas, visual controllers when resizing it with jQuery's resizable() function.
I mean by saying 'visual controllers' these 8 black squares which appear when you click on an image that allow you to resize the image like this example :
I have written the following function that draws 8 squares around the div. When clicking on the div, it gives the aimed visual appearance. When clicking on the div again it removes the 8 squared and removes the resizable() function. It works fine but when resizing the div and clicking on it again to remove the 8 squares it doesn't remove them.
I need to save the canvas state before clicking on before it applies the drawings and restore it when clicking on the canvas again.
$(document).on("click", "canvas", function(eg) {
var thisCanvas = $(this).attr("id");
var theCanvas = document.getElementById(thisCanvas);
var ctx = theCanvas.getContext("2d");
canvasSelected(thisCanvas, ctx);
});
When the user clicks of the canvas it fires the following function:
function canvasSelected(theCanvas, ctx){
var ctxWidth = $("#"+theCanvas).width();
var ctxHeight = $("#"+theCanvas).height();
if($("#"+theCanvas).hasClass("bordering")){
ctx.restore();
$("#"+theCanvas).addClass("defaultBorder");
$("#"+theCanvas).removeClass("bordering");
ctx.beginPath();
ctx.clearRect(0,0,6,6);
ctx.clearRect(ctxWidth- 6,0,6,6);
ctx.clearRect(ctxWidth/2,0,6,6);
ctx.clearRect(0,ctxHeight- 6,6,6);
ctx.clearRect(ctxWidth- 6,ctxHeight- 6,6,6);
ctx.clearRect(ctxWidth/2,ctxHeight- 6,6,6);
ctx.clearRect(0,ctxHeight/2,6,6);
ctx.clearRect(ctxWidth- 6,ctxHeight/2,6,6);
$("#"+theCanvas).resizable("option", "disabled", true);
}else{
ctx.save();
$("#"+theCanvas).removeClass("defaultBorder");
$("#"+theCanvas).addClass("bordering");
ctx.beginPath();
ctx.fillStyle = "black";
ctx.fillRect(0,0,6,6);
ctx.fill();
ctx.fillRect(ctxWidth- 6,0,6,6);
ctx.fill();
ctx.fillRect(ctxWidth/2,0,6,6);
ctx.fill();
// bottom rects..
ctx.fillRect(0,ctxHeight- 6,6,6);
ctx.fill();
ctx.fillRect(ctxWidth- 6,ctxHeight- 6,6,6);
ctx.fill();
ctx.fillRect(ctxWidth/2,ctxHeight- 6,6,6);
ctx.fill();
// middle rects
ctx.fillRect(0,ctxHeight/2,6,6);
ctx.fill();
ctx.fillRect(ctxWidth- 6,ctxHeight/2,6,6);
ctx.fill();
$("#"+theCanvas).resizable();
$("#"+theCanvas).resizable("option", "disabled", false);
}
}
here is the jsfiddle
Your problem is how the canvas is being re-sized via resize(). The size is being changed in terms of how large the canvas is, but it will not change the size of the coordinate system. Your initial width and height of 550x350 stay the same.
Live Demo
All I did was add the following to your canvasSelected event,
// get the canvas element
var canvas = document.getElementById(theCanvas);
// change the pixel dimensions to match the css width and height.
canvas.width = ctxWidth;
canvas.height = ctxHeight;
This ensures the pixel dimensions will be updated as well. Just remember its usually a bad idea to re-size the canvas element using CSS as you will get unexpected results.
The above will cause you to have to redraw your graphics however. So another method is just to keep track of the original width, and height, of your canvas and use those values like the following fiddle does.
Live Demo 2
In this example I just made height and width global so they would always be referenced, however I imagine you can use the demo provided to come up with a better way of keeping track of the original height and width of the element.
Also note, jQuery's width() and height() are not the same as calling width and height on the canvas element. jQuery's methods re-size the element using its style properties.
I have currently two circles in a <canvas> tag with HTML5 & JavaScript.
Now I'm trying to add an image (done) that changes based on mouse-over and click.
It's basically an implementation of a play / pause button with an extra color change when the user mouse-overs the button.
I can't seem to figure out how events work on shapes in HTML5 since they are not objects ... Here is my code at the moment :
window.onload = function() {
var canvas = document.getElementsByTagName('canvas')[0];
var context = canvas.getContext('2d');
var centerX = canvas.width / 2;
var centerY = canvas.height / 2;
//Outer circle
context.beginPath();
context.arc(centerX, centerY, 150, 0, Math.PI * 2, false);
context.fillStyle = "#000";
context.fill();
context.stroke();
//Inner cicle
context.beginPath();
context.arc(centerX, centerY, 75, 0, Math.PI * 2, false);
context.fillStyle = "#fff";
context.fill();
context.stroke();
//Play / Pause button
var imagePlay = new Image();
var imageHeight = 48/2;
imagePlay.onload = function() {
context.drawImage(imagePlay, centerX - imageHeight, centerY - imageHeight);
};
imagePlay.src = "images/play.gif";
}
How to handle events on shapes created with <canvas>?
How to clean-up / remove images on the <canvas> when replacing it with another one?
There is technically no way to register mouse events on canvas-drawn shapes. However, if you use a library, like Raphael (http://raphaeljs.com/), it can keep track of shape positions and thus figure out what shape is receiving the mouse event. here's an example:
var circle = r.circle(50, 50, 40);
circle.attr({fill: "red"});
circle.mouseover(function (event) {
this.attr({fill: "red"});
});
As you can see, it's very simple this way. For modifying shapes, this library will also come in handy. Without it you would need to remember how to redraw everything each time you make a change
Well The simple answer is you can't. You either will have to find the coordinates of the click event and calculate whether you want to perform an option or not or you can use area and map tags and overlay the canvas element with it. To change a canvas use the clearRect function to draw paint a rectangle over everything and then redraw what you want.
There is no "built-in" way of keeping track of shapes drawn on the canvas. They are not treated as objects, but rather just pixels in an area. If you want to handle events on shapes drawn on the canvas, you would need to keep track of the area each shape covers, and then determine which shape you're triggering the event for based on the mouse position.
You can just draw over other shapes if you want to replace it with something. You might want to take a look at globalCompositeOperation.
If you want to treat your drawings as objects, I would recommend using SVG instead of canvas.
Another option is to use buttons, and then style them using CSS.
Basically, what you're doing now really wasn't the intended purpose or use of the canvas. It's like using a pencil to hammer in nails - you're using the wrong tool for the job.
While it's true that you cannot create click events for objects drawn on the canvas there is a workaround: Wrap the canvas in a DIV tag and then add the images within the DIV tag above the CANVAS tag.
<div id="wrapper">
<img src="img1.jpg" id="img1"></img>
<canvas id="thecanvas"></canvas>
</div>
Then use CSS to make the images position:absolute and use left:*px and top:*px attributes to place the image over the canvas where you would have normally drawn it.
#img1{
position:absolute;
left: 10px;
top: 10px;
}
You can then add click events to the image which is placed over the canvas giving the impression that you are clicking on the canvas(the below example uses the jQuery click() function)
$( "#img1" ).click(function(){
alert("Thanks for clicking me");
});
You can cast a ray into the canvas and manually test your images for intersection with the ray. You should look at it like you press, and send a ray into the screen. You should write a
objectsUnderPoint( x, y );
function that returns an array of all the images that intersect with the ray at x, y.
This is the only real right answer, and this is how it is usually done in 3D engines as well.
I am using JQuery-UI draggables and droppables to clone elements onto a div.
What is the best way to draw a line between elements on a page using JQuery.
What is the best way to refresh lines on the page? I will have multiple lines on the page and only want to update a particular line rather than refresh every line.
I now have this working.
In my experience, don't use jquery.svg, it may have been the pressure to solve it without learning my way around another plugin, but I found it more hassle than it was worth and caused compatibilty issues.
It's possible to solve using the HTML5 canvas and excanvas compatibility script, and a great html5 walkthrough, BUT because of how the HTML5 canvas works, it requires that all the linse on the canvas are destroyed and redrawn if a line needs to be removed or its position needs to be updated.
The elements that I draw lines between are inside a parent element that represents a relationship. The child elements represent a start and end, so I can redraw all of these relationships by getting a collection of the parents using e.g. $('.relationshipClass') and interrogating the set's elements' children to get the points of the line.
To use this code you will have to come up with an approach to easily get the line points available to redraw.
Markup:
Nice and simple, a html <div> to hold any elements that are drawn between (most probably JQuery UI draggables), and a <canvas> that will be in the same position
<div id="divCanvasId" class="divCanvasClass"></div>
<canvas id="html5CanvasId"></canvas>
CSS:
Don't control the <canvas> element's width with CSS, see Question on Canvas streching. Position the <canvas> in the same position as the <div> and behind it (with the z-index), this will show the lines up behind the <div> and prevent the <canvas> from blocking any drag and drop interactions with the <div>'s children.
canvas
{
background-color: #FFFFFF;
position: absolute;
z-index: -10;
/* control height and width in code to prevent stretching */
}
Javascript approach:
Create utility methods: the example code is inside a JQuery plugin that contains:
A helper function to reset the canvas (changing the width will delete all of
A helper function to draw lines between two elements
A function that draws lines between all the elements that require one
When you add a new line or adjust the position of a line, you destroy the existing lines and draw all of the lines.
You can put the code below into conventional functions or leave as a plugin.
Javascript code:
N.B. not tested after anonymisation.
$(document).ready(function () {
$.fn.yourExt = {
_readjustHTML5CanvasHeight: function () {
//clear the canvas by readjusting the width/height
var html5Canvas = $('#html5CanvasId');
var canvasDiv = $('#divCanvasId');
if (html5Canvas.length > 0) {
html5Canvas[0].width = canvasDiv.width();
html5Canvas[0].height = canvasDiv.height();
}
}
,
//uses HTML5 <canvas> to draw line representing relationship
//IE support with excanvas.js
_drawLineBetweenElements: function (sourceElement, targetElement) {
//draw from/to the centre, not the top left
//don't use .position()
//that will be relative to the parent div and not the page
var sourceX = sourceElement.offset().left + sourceElement.width() / 2;
var sourceY = sourceElement.offset().top + sourceElement.height() / 2;
var targetX = targetElement.offset().left + sourceElement.width() / 2;
var targetY = targetElement.offset().top + sourceElement.height() / 2;
var canvas = $('#html5CanvasId');
//you need to draw relative to the canvas not the page
var canvasOffsetX = canvas.offset().left;
var canvasOffsetY = canvas.offset().top;
var context = canvas[0].getContext('2d');
//draw line
context.beginPath();
context.moveTo(sourceX - canvasOffsetX, sourceY - canvasOffsetY);
context.lineTo(targetX - canvasOffsetX, targetY - canvasOffsetY);
context.closePath();
//ink line
context.lineWidth = 2;
context.strokeStyle = "#000"; //black
context.stroke();
}
,
drawLines: function () {
//reset the canvas
$().yourExt._readjustHTML5CanvasHeight();
var elementsToDrawLinesBetween;
//you must create an object that holds the start and end of the line
//and populate a collection of them to iterate through
elementsToDrawLinesBetween.each(function (i, startEndPair) {
//dot notation used, you will probably have a different method
//to access these elements
var start = startEndPair.start;
var end = startEndPair.end;
$().yourExt._drawLineBetweenElements(start, end);
});
}
You can now call these functions from event handlers (e.g. JQuery UI's drag event) to draw lines.
Make a hr or div with 1px height set background, and animate its width using jquery when needed.