I'm using heatmap.js in Google Apps Script and I am trying to resize the heatmap canvas (where I can actually plot points) to fit an image. How can I change the javascript or styling to make this work?
CODE: https://jsfiddle.net/nateomardavis/51h2guL9/
The wrapper has a black border.
The heatmap div has a blue border.
The canvas has a red border.
How it Should Work: I should be able to mark the heatmap anywhere on the image. The wrapper, heatmap div and canvas should all be sized based on the size of the image.
How it Currently Works: I can only mark the heatmap inside the blue/black border. My javascript resizes the canvas to fit the image, but not the bounds of the heatmap.
What I've Tried: You can see in the fiddle how I've tried to change what the javascript is resizing (lines 88-90).I also thought about using the using the image as the background-image of the heatmap div but the sizing wasn't right and I wanted to be able to save the heatmap and image as one layer (for printing/saving). I also tried changing the size of the image (line 101) but anything above 55% would cut the image off.
The screenshot below is just for reference, but the full working code is in the fiddle. I have it all under HTML because that's how it works in Google Apps Script.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script type="text/javascript" src="https://www.patrick-wied.at/static/heatmapjs/assets/js/heatmap.min.js"></script>
<style>
html, body {
width: 100%;
height: 100%;
}
html {
color: #333;
font-family: sans-serif;
font-size: 16pt;
font-weight: 100;
line-height: 1.5em;
}
.wrapper {
width: 100%;
height:100%;
background:rgba(0,0,0,.03);
border:3px solid black;
}
.heatmap {
width:100%;
height:100%;
background-position: top;
background-repeat: no-repeat;
background-size: contain;
position: relative;
border:3px solid blue;
}
.canvas {
border:3px solid red;
}
</style>
</head>
<body>
<button class="clear">Clear</button>
<button onclick="printPage()" class="print-btn no-print" style="">Print</button>
<p id="coords"></p>
<div id="wrapper" class="wrapper"> <!-- black box on web app-->
<div id="heatmap" class = "heatmap" style="position: relative;"> <!-- blue box on web app-->
<canvas id="canvas" class="canvas"></canvas> <!-- red box on web app-->
</div>
</div>
<script>
//CREATE HEATMAP
window.onload = function() {
var heatmapInstance = h337.create({
container: document.querySelector('.heatmap'),
gradient: {
'.25': 'black',
'.5': 'red',
'.75': 'white'
},
radius: 20
});
document.querySelector('.wrapper').onclick = function(ev) {
heatmapInstance.addData({
x: ev.layerX,
y: ev.layerY,
value: 1
});
};
document.querySelector('.clear').onclick = function() {
//heatmapInstance._renderer._clear();
heatmapInstance.setData({data:[]});
};
//RESIVE CANVAS TO FIT IMAGE
//http://jsfiddle.net/6ZsCz/1135/
var canvas = document.getElementById('canvas'); //v1
//var canvas = document.getElementById('heatmap'); //v2
//var canvas = document.getElementById('wrapper'); //v3
var ctx = canvas.getContext('2d');
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
var imageObj = new Image();
imageObj.onload = function() {
canvas.width = imageObj.width; // version 2
canvas.height = imageObj.height; // version 2
ctx.drawImage(imageObj,0,0); // version 2
//ctx.drawImage(imageObj, 0, 0, imageObj.width * 0.5, imageObj.height * 0.5); //version 1
};
imageObj.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Soccer_Field_Transparant.svg/1200px-Soccer_Field_Transparant.svg.png';
};
//MOUSE COORDINATES
//works inside heatmap
function getCursorPosition(canvas, event) {
const rect = canvas.getBoundingClientRect()
const x = event.clientX - rect.left
const y = event.clientY - rect.top
var coor = "X coords: " + x + ", Y coords: " + y;
document.getElementById("coords").innerHTML = coor;
}
const canvas = document.querySelector('.heatmap')
canvas.addEventListener('mousedown', function(e) {
getCursorPosition(canvas, e)
})
function printPage() {
window.print();
}
</script>
</body>
</html>
After lots of tinkering, I was able to figure it out: https://jsfiddle.net/nateomardavis/p1kwrhmv
I used img and gave it a background-image with CSS.
I then set the heatmap layer to relative and the image layer to absolute using the solution HERE.
Last, I then set the image layer css using the method outlined HERE.
.heatmap {
background-size: contain;
background-repeat: no-repeat;
width: 100%;
height: 300%; /*if the image is bigger than screen, this is needed */
position: relative;
}
.backgroundIMG {
background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Soccer_Field_Transparant.svg/1200px-Soccer_Field_Transparant.svg.png');
background-size: contain;
background-repeat: no-repeat;
position: absolute;
width: 100%;
height: 0;
padding-top: 151.33%;
}
Related
I have been trying to make an application like open-board. For that, I am using canvas API. The problem is that the lines are drawn below the clicked point. I have already subtracted offset top and offset left but still facing the issue.
Also, one thing that I have noticed, the distance between the clicked point and the drawn figure increases as the clicked point goes farther away from the origin. Like this: image
here is my code:
canvas.addEventListener("mousedown",(e)=>{
let x = drawArea.offsetTop
let y = drawArea.offsetLeft
if(lineToolSelected == false)
{
lineToolSelected = true;
tool.beginPath()
tool.moveTo(e.clientX-y,e.clientY-x)
}else if( lineToolSelected == true){
lineToolSelected = false
tool.lineTo(e.clientX-y,e.clientY-x)
tool.stroke()
}
})
.draw-area{
/* position: fixed; */
position: absolute;
top:0rem;
left:0rem;
height: 175px;
width: 350px;
background:white;
}
.draw-area-cont{
/* position: fixed; */
position: absolute;
top:10rem;
left:2rem;
width:74rem;
background:#eeeded;
height: 37rem;
overflow: hidden;
/* z-index:1; */
}
#canvas{
/* position: fixed; */
height: 100%;
background-color: white;
width: 100%;
color: white;
}
<div class="draw-area-cont">
<div class="draw-area">
<canvas id="canvas"></canvas>
</div>
</div>
If anyone encounters the same problem, I did this:
var scale = 2;
canvas.height = canvas.offsetHeight * scale;
canvas.width = canvas.offsetwidth * scale;
I don't think you even need to multiply with scale, just doing this should work too:
canvas.height = canvas.offsetHeight;
canvas.width = canvas.offsetwidth;
I multiplied with scale to increase the resolution of the image.
I am using Jcrop to crop Images, it is working fine with small size images, but Whenever I try to put large image like 2080x2080 or 1600x1600 it covers all my screen, also It cannot be handle with CSS like width and height control in Image tag
Seeking Solution/Suggestion -
Any method to put this image in container/div and then zoom-in/out with mouse event ?
Any method to handle large size image in a fix container and crop so that image maintain it's quality.
P.s: you can try with large image in below working example and see how it behaves with big Image.
var jcp;
var save = document.getElementById('save');
var imageLoader = document.getElementById('imageLoader');
var img = document.getElementById("target");
imageLoader.onchange = function handleImage(e) { //handling our image picker <input>:
var reader = new FileReader();
reader.onload = function(event) {
img.src = event.target.result;
}
reader.readAsDataURL(e.target.files[0]);
}
save.onclick = function() {
if (jcp && jcp.active) {
var i = 0;
for (area of jcp.crops) {
i++;
canvas = document.createElement("canvas");
canvas.setAttribute('width', area.pos.w);
canvas.setAttribute('height', area.pos.h);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x, area.pos.y, area.pos.w, area.pos.h, 0, 0, area.pos.w, area.pos.h);
temp = document.createElement('a');
temp.setAttribute('download', 'area' + i + '.jpg');
temp.setAttribute('href', canvas.toDataURL("image/jpg").replace("image/jpg", "image/octet-stream"));
temp.click();
}
}
};
Jcrop.load('target').then(img => {
jcp = Jcrop.attach(img, {
multi: true
});
});
body {
margin: 0px;
padding: 0px;
background-color: #ededed;
}
.cropapp {
position: absolute;
width: 100%;
height: 100%;
left: 5%;
background: lightgray;
}
.cropbox {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
}
div {
position: relative;
overflow: hidden;
}
<head>
<title>Jcrop Example</title>
<link href="https://unpkg.com/jcrop#3.0.1/dist/jcrop.css" rel="stylesheet" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://unpkg.com/jcrop#3.0.1/dist/jcrop.js"></script>
</head>
<body>
<div style="width:400px; margin:10px auto; padding:10px 0; overflow:hidden;"><a style="float:right; display:block; padding:10px 15px; color:#fff; background-color:#237dbd; font-size: 14px; font-weight:bold; border-radius:5px;" id="save">Save</a>
<input type="file" id="imageLoader" name="imageLoader" />
<!-- add this for file picker -->
</div>
<div>
<h1 style="font-family:Helvetica,sans-serif;">
Image Crop <span style="color:lightgray;"></span>
</h1>
<img id="target" style="background-size: cover !important;">
</div>
</body>
You can add any fixed width or height value to your <img id="target"/>. Just calculate and apply the change ratio to areas:
canvas = document.createElement("canvas");
displayWidth=img.width;
displayHeight=img.height;
realWidth = img.naturalWidth;
realHeight = img.naturalHeight;
widthRatio = Math.round(realWidth/displayWidth);
heightRatio = Math.round(realHeight/displayHeight);
width=area.pos.w*widthRatio
height=area.pos.h*heightRatio
canvas.setAttribute('width', width);
canvas.setAttribute('height', height);
ctx = canvas.getContext("2d");
ctx.drawImage(img, area.pos.x*widthRatio, area.pos.y*heightRatio, width, height, 0, 0, width, height);
and <img width="500" id="target"/> (or with css #target{width:500px})
I have a canvas like so:
But when resizing the window and redrawing, you can get anything in between.
I keep the aspect ratio with this code:
function setCanvasSize(){
ctx.canvas.width = (window.innerWidth)*0.8;
ctx.canvas.height= (window.innerWidth)*0.5;
}
setCanvasSize()
$(window).resize(function(){
setCanvasSize()
})
and that keeps it nice and clean, the same-looking size, but how many pixels are in there changes, so when my program draws, it's always in a different spot, although the aspect is the same, so it looks really weird.
Any way that I can somehow tweak how I draw things, or a way to keep how many pixels there are?
Edit: code snippet to reproduce
const ctx = document.getElementById('game').getContext("2d");
$('#game').css('width', '100%');
$(window).resize(function(){
$('#game').height($('#game').width() / 2.031);
});
const imageAt=((href,x,y,sizex,sizey)=>{
var img = new Image();
img.addEventListener('load', function() {
ctx.drawImage(img, x, y, sizex, sizey);
}, false);
img.src = href
})
imageAt('https://b.thumbs.redditmedia.com/bZedgr0gq7RQBBnVYVc-Nmzdr-5vEUg4Dj8nTrMb7yA.png',0,0,50,50)
#game{
padding-left: 0;
padding-right: 0;
display: block;
margin: 0;
border:5px solid green;
padding:0;
}
#vertical-center {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: black;
padding:0;
}
body{
background-color: black;
padding:0;
}
<html>
<head>
<title>Tiled Canvas Engine Test 1</title>
<meta name="viewport" content="width=device-width, initial-scale=0.75">
<link rel="stylesheet" href="./index.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="engine.js"></script>
<script defer src="script.js"></script>
</head>
<body>
<div id="vertical-center">
<canvas id="game"></canvas>
</div>
</body>
</html>
In order to render properly the canvas element needs to have its width and height attributes set explicitly. This will define the viewport into which graphics will be draw. The style attributes for width and height which is what jQuery sets via the width() and height() functions will only stretch the viewport to fit. (Note: for high DPI displays, i.e. Retina, you will want to double the dimensions when specifying the attributes).
The critical change is this:
// Updates the actual width and height attributes
canvas.width = $('#game').width() * window.devicePixelRatio;
canvas.height = $('#game').height() * window.devicePixelRatio;
The snippet below also handles clearing the canvas, redrawing the image, and saving the Image once loaded:
const ctx = document.getElementById('game').getContext("2d");
$('#game').css('width', '100%');
$(document).ready(handleResize);
$(window).resize(handleResize);
function handleResize() {
// sets the style.height and style.width for the canvas
$('#game').height($('#game').width() / 2.031);
let canvas = $('#game').get(0);
// Updates the actual width and height attributes
canvas.width = $('#game').width() * window.devicePixelRatio;
canvas.height = $('#game').height() * window.devicePixelRatio;
// redraw
if (img) {
redraw();
}
}
var img;
const imageAt = ((href, x, y, sizex, sizey) => {
img = new Image();
img.addEventListener('load', function() {
ctx.drawImage(img, x, y, sizex, sizey);
}, false);
img.src = href
})
imageAt('https://b.thumbs.redditmedia.com/bZedgr0gq7RQBBnVYVc-Nmzdr-5vEUg4Dj8nTrMb7yA.png', 0, 0, 50 * window.devicePixelRatio, 50 * window.devicePixelRatio)
function redraw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(img, 0, 0, 50 * window.devicePixelRatio, 50 * window.devicePixelRatio);
}
#game {
padding-left: 0;
padding-right: 0;
display: block;
margin: 0;
border: 5px solid green;
padding: 0;
}
#vertical-center {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background: black;
padding: 0;
}
body {
background-color: black;
padding: 0;
}
<html>
<head>
<title>Tiled Canvas Engine Test 1</title>
<meta name="viewport" content="width=device-width, initial-scale=0.75">
<link rel="stylesheet" href="./index.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="engine.js"></script>
<script defer src="script.js"></script>
</head>
<body>
<div id="vertical-center">
<canvas id="game"></canvas>
</div>
</body>
</html>
First look at my code:
IMAGE:
<img class="small" src="http://thecodeplayer.com/uploads/media/iphone.jpg" width="200"/>
BACKGROUND URL:
<div class="small" style="background-image: url('http://thecodeplayer.com/uploads/media/iphone.jpg')" ></div>
In the IMAGE I can use an attribute src in jquery
attr("src");
But how can I do it to in a background Image URL? I made this code and added an attribute but it's not working, any ideas?
<div class="small" style="background-image: url(attr(data-image-src='http://thecodeplayer.com/uploads/media/iphone.jpg')" ></div>
attr("data-image-src");
Now the reason why I want this to happen is that if you will look in this CodePen:
codepen
The sample code is an image and they used an attr(src) in jquery code, but I want it to be a background image URL, not an image.
To get the background-image in jQuery you can use:
var urlFull = $('.small').css('background-image');
//You will get the full value here
url = urlFull.split('"');
//But for using it properly you have to split it and get the url with `"` which will be holding the url.
console.log(url[1]); //You will get URL here
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="small" style="background-image: url('http://thecodeplayer.com/uploads/media/iphone.jpg')"></div>
Now for your real issue:
$(document).ready(function() {
var native_width = 0;
var native_height = 0;
var urlFull = $('.small').css('background-image');
//You will get the full value here
url = urlFull.split('"');
//But for using it properly you have to split it and get the url with `"` which will be holding the url.
//console.log(urlFull);
$(".large").css("background-image", urlFull);
//Now the mousemove function
$(".magnify").mousemove(function(e) {
//When the user hovers on the image, the script will first calculate
//the native dimensions if they don't exist. Only after the native dimensions
//are available, the script will show the zoomed version.
if (!native_width && !native_height) {
//This will create a new image object with the same image as that in .small
//We cannot directly get the dimensions from .small because of the
//width specified to 200px in the html. To get the actual dimensions we have
//created this image object.
var image_object = new Image();
image_object.src = url[1];
//This code is wrapped in the .load function which is important.
//width and height of the object would return 0 if accessed before
//the image gets loaded.
native_width = image_object.width;
native_height = image_object.height;
} else {
//x/y coordinates of the mouse
//This is the position of .magnify with respect to the document.
var magnify_offset = $(this).offset();
//We will deduct the positions of .magnify from the mouse positions with
//respect to the document to get the mouse positions with respect to the
//container(.magnify)
var mx = e.pageX - magnify_offset.left;
var my = e.pageY - magnify_offset.top;
//Finally the code to fade out the glass if the mouse is outside the container
if (mx < $(this).width() && my < $(this).height() && mx > 0 && my > 0) {
$(".large").fadeIn(100);
} else {
$(".large").fadeOut(100);
}
if ($(".large").is(":visible")) {
//The background position of .large will be changed according to the position
//of the mouse over the .small image. So we will get the ratio of the pixel
//under the mouse pointer with respect to the image and use that to position the
//large image inside the magnifying glass
var rx = Math.round(mx / $(".small").width() * native_width - $(".large").width() / 2) * -1;
var ry = Math.round(my / $(".small").height() * native_height - $(".large").height() / 2) * -1;
var bgp = rx + "px " + ry + "px";
//Time to move the magnifying glass with the mouse
var px = mx - $(".large").width() / 2;
var py = my - $(".large").height() / 2;
//Now the glass moves with the mouse
//The logic is to deduct half of the glass's width and height from the
//mouse coordinates to place it with its center at the mouse coordinates
//If you hover on the image now, you should see the magnifying glass in action
$(".large").css({
left: px,
top: py,
backgroundPosition: bgp
});
}
}
})
})
/*Some CSS*/
* {
margin: 0;
padding: 0;
text-align: center
}
.magnify {
width: 200px;
margin: 50px auto;
position: relative;
cursor: none;
display: inline-block;
}
/*Lets create the magnifying glass*/
.large {
width: 175px;
height: 175px;
position: absolute;
border-radius: 100%;
/*Multiple box shadows to achieve the glass effect*/
box-shadow: 0 0 0 7px rgba(255, 255, 255, 0.85), 0 0 7px 7px rgba(0, 0, 0, 0.25), inset 0 0 40px 2px rgba(0, 0, 0, 0.25);
/*hide the glass by default*/
display: none;
background-repeat: no-repeat;
}
/*To solve overlap bug at the edges during magnification*/
.small {
display: block;
width: 200px;
height: 400px;
float: left;
background-size: contain;
background-repeat: no-repeat;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- Lets make a simple image magnifier -->
<div class="magnify">
<!-- This is the magnifying glass which will contain the original/large version -->
<div class="large"></div>
<!-- This is the small image -->
<div class="small" style="background-image:url('http://thecodeplayer.com/uploads/media/iphone.jpg')" width="200" />
</div>
</div>
Codepen if you want.
I hope this woks for you.
I think you have missed a Single quote in your attribute.
Change
<div class="small" style="background-image: url(http://thecodeplayer.com/uploads/media/iphone.jpg')" ></div>
to
<div class="small" style="background-image: url('http://thecodeplayer.com/uploads/media/iphone.jpg')" ></div>
Hope this helps.
First you have to set '' to url
<div class="small" style="background-image: url('http://thecodeplayer.com/uploads/media/iphone.jpg')" ></div>
To get url of background-image:
var url=$('.small').attr('src')
$('.large').css('background-image',"url('"+ url + "')");
.large{
background-repeat: no-repeat;
width: 175px;
height: 175px;
background-size: cover;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img class="small" src="http://thecodeplayer.com/uploads/media/iphone.jpg" width="200"/>
<div class="large"></div>
Im trying to get a div called touch-container to hold a canvas with 3 div's on top. I'm sizing them using javascript as well.
The problem is that each of the 3 div's have a margin which takes up the rest of the space in the container even though i specifically state 0 margin.
Here is a jsFiddle, if you inspect, you can see the margin Jsfiddle
HTML:
<div id = "touch-container">
<div id = "touch-left" class = "touch-control"></div>
<div id = "touch-middle" class = "touch-control"></div>
<div id = "touch-right" class = "touch-control"></div>
<canvas id = "canvas" width = "960" height = "560"></canvas>
</div>
CSS:
body {
margin: 0;
}
#touch-container{
width: 964px;
height: 560px;
display: inline-block;
margin: 0;
}
.touch-control{
position: relative;
margin: 0;
}
#touch-left{
background-color: red;
}
#touch-middle{
background-color: green;
}
#touch-right{
background-color: blue;
}
JS:
var leftTouch = document.getElementById('touch-left');
var upTouch = document.getElementById('touch-middle');
var rightTouch = document.getElementById('touch-right');
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext("2d");
var width = (canvas.width > canvas.height ? canvas.width : canvas.height);
var height = (canvas.height > canvas.width ? canvas.width : canvas.height);
leftTouch.style.width = (width/ 4) + 'px';
leftTouch.style.height = height + 'px';
upTouch.style.width = (width/ 2) + 'px';
upTouch.style.height = height + 'px';
rightTouch.style.width = (width/ 4) + 'px';
rightTouch.style.height = height + 'px';
ctx.fillRect(0,0,canvas.width, canvas.height);
I've colored them in so it should be a black square on the bottom, then from left to right, a red, green and blue square that takes up 25%, 50% 25% of the container respectively so you wont even be able to see the black canvas.
You didn't add any float in your elements(squares) so the squares don't know how they should be located, You most use float:left; to force squares to be in a same line and display:inline-block; wont works in your case (it just works for text only):
Jsfiddle
css:
#touch-container{
width: 964px;
height: 560px;
display: block;/* new update */
margin: 0;
}
#touch-left{
background-color: red;
float:left;/* new update from here to below */
display:block;
}
#touch-middle{
background-color: green;
float:left;/* new update from here to below */
display:block;
}
#touch-right{
background-color: blue;
float:left;/* new update from here to below */
display:block;
}
Margin refers to the spacing between block and inline-block level elements. Since divs are block elements, they stack on top of each other, and margin: 0 removes any space between them.
If you are looking to place the divs on top of the canvas element, you can achieve that with absolute positioning.
.touch-control {
position: absolute;
top: 0;
}
Then you can use your javascript to set the 'left' property of each div to where you want it, or positioning them individually with the css.