Javascript html2canvas cant get background - javascript

Some issues using Javascript library html2canvas. The problem is that html2canvas is getting "div2" with a transparent (sometimes white) background, I want to include the body background (or is behind this div) on "div2".
<div id="div2" style="width: 150px; height: 50px;"></div>
html2canvas(document.getElementById("div2"), {
onrendered: function(canvas) {
var photostring = canvas.toDataURL("image/png");
console.log(photostring);
}
});

Logically the rendering works from the element you select.
So will not get the background, unless you select the "parent element" or "root element".
I have two solutions:
1 - Using Javascript/Jquery you can capture X and Y position from div#target
then you set background-image and background-position in div#target by X/Y position
2 - You capture <body> with html2canvas, then using canvas/javascript api you crop image by X/Y position and Width/Height from div#target, see example: #242 (comment)
Note: set width/height/x/y in these variables (see example):
var targetDiv = $("#target");
var sourceX = targetDiv.position().left;/*X position from div#target*/
var sourceY = targetDiv.position().top;/*Y position from div#target*/
var sourceWidth = targetDiv.width();/*clientWidth/offsetWidth from div#target*/
var sourceHeight = targetDiv.height();/*clientHeight/offsetHeight from div#target*/
Get background-image in parent/root element:
https://github.com/niklasvh/html2canvas/commit/281e6bbedf9f611846eba3af4d256eb97f608aa2
Crop canvas:
https://github.com/niklasvh/html2canvas/issues/242#issuecomment-20875688

Related

Trying to drag and drop onto an SVG canvas with accurate coordinates

I am trying to implement drag and drop an image element onto an SVG canvas but the elements are not in the same div. I have tried to use clientX and clientY properties but they are out by about 20 pixels when I come to drop the element. I think the problem is that the SVG coordinates work from the top of the root SVG element but drag and drop coordinates are from the visible view port. When I try to release the rock image on the canvas it just seems to go to any random place on it and sometimes off the canvas completely.
I have uploaded all my code to https://github.com/kiwiheretic/gravity
(It shouldn't be too difficult to just clone the repo and open index.html within the browser to see the problem.)
The relevant HTML code is as follows:
<div style="background-color:black; width: min-content;">
<svg id="space" width="600" height="400" xmlns="http://www.w3.org/2000/svg" onload="makeDraggable(evt)">
</svg>
</div>
<div id="object-block-1" class="objects">
<div class="box">
<img src="asteroid.png" alt="asteroid">
</div>
</div>
</div>
The relevant javascript code is:
var objblock = document.getElementById("object-block-1");
objblock.addEventListener("dragstart", function(evt) {
draggedObject = evt.target;
});
objbl
objblock.addEventListener("dragend", function(evt) {
var x = evt.clientX;
var y = evt.clientY;
var src = draggedObject.getBoundingClientRect();
console.log([x,y]);
var space = document.getElementById("space");
var tgt = space.getBoundingClientRect();
if (doesPointCollide(x,y,tgt)) {
//asteroid = makeAsteroid(x, y, 50, 50);
//space.appendChild(asteroid);
}
});
objblock.addEventListener("drag", function(evt) {
var space = document.getElementById("space");
var rect = space.getBoundingClientRect();
var src = draggedObject.getBoundingClientRect();
var x = (src.x - rect.x) + evt.clientX;
var y = (src.y + rect.y) + evt.clientY;
document.getElementsByName("drag-x")[0].value = parseInt(x);
document.getElementsByName("drag-y")[0].value = parseInt(y);
});
I am using the right hand input boxes as kind of a debugging aid. I am not sure why drag-x and drag-y are not resolving to suitable SVG coordinates within the canvas and what needs to be done to fix that.
Any idea what javascript mouse event attributes and element attributes to work out my SVG coordinates for drag and drop?

Circle created using Fabric.js generates oval shape on canvas

I am new to fabric.js, I want to create a circle using fabric and render it on canvas I have done the following code. But this code generates an Oval shaped figure which is not a perfect circle, Please let me know why is this happening.
this.canvasIns = new fabric.Canvas('mySampleCanvas');
var myCircle = new fabric.Circle({
radius:50,
borderColor: 'black',
fill:'#fff'
});
this.canvasIns.add(myCircle);
this.canvasIns.renderAll();
I found the problem due to which I was facing the issue. It was happening because I was dynamically changing the height, width of the canvas using the following code.
Due to which the issue was occurring.
var canvasElement = this.myCanvas.nativeElement;
canvasElement.style.height = canvasElement.nextElementSibling.style.height = this.canvas_css.height;
canvasElement.style.width = canvasElement.nextElementSibling.style.width = this.canvas_css.width;
canvasElement.style.left = canvasElement.nextElementSibling.style.left = this.canvas_css.left;
canvasElement.style.top = canvasElement.nextElementSibling.style.top = this.canvas_css.top;
I removed this code and gave static style to the canvas and its working fine.
Check that the height and width off your html attributes and css are the same. If you use CSS to set the size of the canvas the canvas will be stretched not resized to the CSS Values.
Example:
- Canvas Size: 500:500, your circle: 100:100
- CSS Size: 1000:500
- Canvas size on screen: 1000:500, your circle: 200:100
In my case I solved the problem by adding a <div> around the canvas generated by fabric.js to get the desired width and height values and then setting the canvas size with javascript on pageload:
Html:
<div id="drawingArea">
<canvas id="canvas" class="canvas-layer" height="500" width="500"></canvas>
</div>
Javascript:
var canvas = new fabric.Canvas('canvas');
$(window).on('load', function (e) {
canvas.setHeight($('#drawingArea').height());
canvas.setWidth($('#drawingArea').width());
});

Is SVG resizing this hard ? What am I missing?

I am trying to write code that resizes an SVG overlay on top of an image. The server I am working with returns both the image and via an API, a list of polygon points I need to overlay on top of the image.
This is what it should look like (the image below is a correctly aligned SVG layer). The image size is 1280x720 (I've scaled it down)
What I need to do in my app (ionic v1 app) is to make sure the SVG overlay resizes as the browser window resizes and it seems to be very hard. Here is my approach:
I am trapping a window resize event and when the image is resized, I scale the SVG polygon points relative to the size of the drawn window as it seems there is really no way to "automatically" scale the SVG by the browser like it does with an image.
Here is my code pen as you see it doesn't work as intended when I rescale (and for that matter in when its full size the overlays are not accurate). The overlays don't look accurate and when I resize it all messed up. Can someone help?
Given SO needs a code block for codepen links here it is, but its just easier to look at the codepen if you want to run it
CSS:
.imagecontainer{position:relative; margin:0 auto;}
.zonelayer
{
position:absolute;
top:0;
left:0;
background:none;
}
.zonelayer polygon {
fill-opacity: 0.25;
stroke-width: 2px;
}
.Active {
stroke: #ff0000;
fill: #ff0000;
}
HTML code:
<ion-content>
image:{{disp}}<br/>
<small>points: <span ng-repeat="item in zoneArray">{{item}}</span></small>
<div class="imagecontainer">
<img id="singlemonitor" style="width:100vw; height:100vh;object-fit:contain" ng-src="http://m9.i.pbase.com/o9/63/103963/1/164771719.2SfdldRy.nphzms.jpeg" />
<div class="zonelayer">
<svg ng-attr-width="{{cw}}" ng-attr-height="{{ch}}" class="zonelayer" ng-attr-viewBox="0 0 {{cw}} {{ch}}">
<polygon ng-repeat="item in zoneArray" ng-attr-points="{{item}}" class="Active"/> </polygon>
</svg>
</div>
</div>
</ion-content>
JS controller:
window.addEventListener('resize', liveloaded);
liveloaded();
// credit: http://stackoverflow.com/questions/41411891/most-elegant-way-to-parse-scale-and-re-string-a-string-of-number-co-ordinates?noredirect=1#41411927
function scaleCoords(string, sx, sy) {
var f = [sx, sy];
return string.split(' ').map(function (a) {
return a.split(',').map(function (b, i) {
return Math.round(b * f[i]);
}).join(',');
}).join(' ');
}
function liveloaded()
{
$timeout (function () {
console.log ("IMAGE LOADED");
var img =document.getElementById("singlemonitor");
//var offset = img.getBoundingClientRect();
$scope.cw = img.clientWidth;
$scope.ch = img.clientHeight;
$scope.vx = img.offsetWidth;
$scope.vy = img.offsetHeight;
var rect = img.getBoundingClientRect();
//console.log(rect.top, rect.right, rect.bottom, rect.left);
$scope.disp = img.clientWidth+ "x"+img.clientHeight + " with offsets:"+$scope.vx+"/"+$scope.vy;
$scope.zoneArray = [
"598,70 700,101 658,531 516,436",
"531,243 687,316 663,593 360,717 191,520",
"929,180 1108,248 985,707 847,676",
"275,17 422,45 412,312 271,235",
];
var ow = 1280;
var oh = 720;
for (var i=0; i < $scope.zoneArray.length; i++)
{
var sx = $scope.cw/ow;
var sy = $scope.ch/oh;
$scope.zoneArray[i] = scaleCoords($scope.zoneArray[i],sx,sy);
console.log ("SCALED:"+$scope.zoneArray[i]);
}
});
}
There are a couple of issues with your code.
The main problem is you can't use ng-attr-viewBox, because angular will "normalise" the attribute to lower case. It turns the attribute into viewbox (lower case B) which is (currently) invalid. viewBox is case sensitive.
The solution is to use a special trick of Angular to preserve camel-case. If you use ng-attr-view_box, it will generate the correctly camel-cased attribute name of viewBox.
<svg width="100vw" height="100vh" class="zonelayer" ng-attr-view_box="0 0 {{cw}} {{ch}}">
The other thing is that you are using the wrong width and height values for the viewBox. You need to use the natural/intrinsic image dimensions in your viewBox.
$scope.cw = img.naturalWidth;
$scope.ch = img.naturalHeight;
Link to updated code pen

HTML5 Canvas - Grouping / Attaching an image TO a canvas

Using this JS Fiddle I am able to press a button to add new canvases to the screen...
var next = 4
function addCanvas() {
// create a new canvas element
var newCanvas = document.createElement("canvas");
newCanvas.id = "addedcanvases" + next; //added this to give each new canvas a unique id
next++;
newCanvas.width = canvasWidth;
newCanvas.height = canvasHeight;
// add a context for the new canvas to the contexts[] array
contexts.push(newCanvas.getContext("2d"));
// link the new canvas to its context in the contexts[] array
newCanvas.contextIndex = contexts.length;
// wire up the click handler
newCanvas.onclick = function (e) {
handleClick(e, this.contextIndex);
};
// wire up the mousemove handler
newCanvas.onmousemove = function (e) {
handleMousemove(e, this.contextIndex);
};
// add the new canvas to the page
document.body.appendChild(newCanvas);
}
The problem:
What is the best way to go about grouping / attaching a static image to the top of a canvas (as shown in the image below) so that whenever a new canvas is created in JS Fiddle an image is automatically created with it that is grouped / attached to the top of the new canvas.
This is so that where-ever a new canvas is dynamically created on the page an image is put above that canvas?
There may be an obvious way to do this that I am overlooking? but googling has not thrown up much as all 'image' and 'canvas' searches inevitably relate to actually adding an image to the canvas - which is not what I want to do in this instance.
Here's taking #KaliedaRik's answer and creating your groups using javascript:
http://jsfiddle.net/m1erickson/3EUnc/
The code to create a new group could be something like this:
function newGroup(){
// create a new wrapper div
var div=document.createElement("div");
div.className="wrapper";
// create an img and a canvas element
var img=document.createElement("img");
var br=document.createElement("br");
img.style.width="50px";
img.src="houseicon.png";
var canvas=document.createElement("canvas");
canvas.width=300;
canvas.height=55;
// add the img and canvas elements to the wrapper div
div.appendChild(img);
div.appendChild(br);
div.appendChild(canvas);
// add the wrapper div with its contained img + canvas to the page
document.body.appendChild(div);
}
One possible solution: when you create the canvas element, create a new div at the same time and put the canvas and img tags inside it. By making the div position style relative and the contained canvas and img position styles absolute, you'll be able to place the image wherever it needs to go.
<div style="position: relative">
<canvas style="position: absolute; top: 0px; left: 0px;"></canvas>
<img src="whatever" alt="whatever" style="position: absolute; top: -20px; left: 0px" />
</div>

jQuery and Canvas loading behaviour

After being a long time lurker, this is my first post here! I've been RTFMing and searching everywhere for an answer to this question to no avail. I will try to be as informative as I can, hope you could help me.
This code is for my personal webpage.
I am trying to implement some sort of a modern click-map using HTML5 and jQuery.
In the website you would see the main image and a hidden canvas with the same size at the same coordinates with this picture drawn into it.
When the mouse hovers the main picture, it read the mouse pixel data (array of r,g,b,alpha) from the image drawn onto the canvas. When it sees the pixel color is black (in my case I only check the RED value, which in a black pixel would be 0) it knows the activate the relevant button.
(Originally, I got the idea from this article)
The reason I chose this method, is for the page to be responsive and dynamically change to fit different monitors and mobile devices. To achieve this, I call the DrawCanvas function every time the screen is re-sized, to redraw the canvas with the new dimensions.
Generally, this works OK. The thing is ,there seems to be an inconsistent behavior in Chrome and IE(9). When I initially open the page, I sometimes get no pixel data (0,0,0,0), until i re-size the browser. At first I figured there's some loading issues that are making this happen so I tried to hack it with setTimeout, it still doesn't work. I also tried to trigger the re-size event and call the drawCanvas function at document.ready, still didn't work.
What's bothering me is most, are the inconsistencies. Sometimes it works, sometimes is doesn't. Generally, it is more stable in chrome than in IE(9).
Here is the deprecated code:
<script type="text/javascript">
$(document).ready(function(){setTimeout(function() {
// Get main image object
var mapWrapper = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
// Create a hidden canvas the same size as the main image and append it to main div
var canvas = document.createElement('canvas');
canvas.height = mapWrapper.clientHeight;
canvas.width = mapWrapper.clientWidth;
canvas.fillStyle = 'rgb(255,255,255)';
canvas.style.display = 'none';
canvas.id = 'hiddencvs';
$('#map_wrapper').append(canvas);
// Draw the buttons image into the canvas
drawCanvas(null);
$("#map_wrapper").mousemove(function(e){
var canvas = document.getElementById('hiddencvs');
var context = canvas.getContext('2d');
var pos = findPos(this);
var x = e.pageX - pos.x;
var y = e.pageY - pos.y;
// Get pixel information array (red, green, blue, alpha)
var pixel = context.getImageData(x,y,1,1).data;
var red = pixel[0];
var main_img = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
if (red == 0)
{
...
}
else {
...
}
});
},3000);}); // End DOM Ready
function drawCanvas(e)
{
// Get context of hidden convas and set size according to main image
var cvs = document.getElementById('hiddencvs');
var ctx = cvs.getContext('2d');
var mapWrapper = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
cvs.width = mapWrapper.clientWidth;
cvs.height = mapWrapper.clientHeight;
// Create img element for buttons image
var img = document.createElement("img");
img.src = "img/main-page-buttons.png";
// Draw buttons image inside hidden canvas, strech it to canvas size
ctx.drawImage(img, 0, 0,cvs.width,cvs.height);
}
$(window).resize(function(e){
drawCanvas(e);
}
);
function findPos(obj)
{
...
}
</script>
I'd appreciate any help!
Thanks!
Ron.
You don't wait for the image to be loaded so, depending on the cache, you may draw an image or not in the canvas.
You should do this :
$(function(){
var img = document.createElement("img");
img.onload = function() {
var mapWrapper = document.getElementById('map_wrapper').getElementsByTagName('img').item(0);
...
// your whole code here !
...
}
img.src = "img/main-page-buttons.png";
});

Categories

Resources