I'm trying to implement a behaviour by which I can add an initial image to a canvas (placeholder) and then subsequently I can then choose a different image to replace that image - that new image should then scale itself to "fit" within the bounds of the old image. For example if I choose a portrait image as the "placeholder" and then choose a landscape image to replace it, Id expect the landscape image to first scale its width and maintain aspect ratio, and then vertically align itself within the boundaries of the placeholder dimensions.
Here is a jsfiddle of what I have working so far: https://jsfiddle.net/cgallagher/co8dg527/1/
and here is the code:
var canvas = new fabric.Canvas('stage');
var cw = canvas.getWidth()
var ch = canvas.getHeight()
var _currentImage;
function setProductImage(url){
var oldWidth;
var oldHeight;
var oldLeft;
var oldTop;
fabric.Image.fromURL(url, function(img) {
if (_currentImage) {
oldWidth = _currentImage.getScaledWidth()
oldHeight = _currentImage.getScaledHeight()
oldLeft = _currentImage.left
oldTop = _currentImage.top
canvas.remove(_currentImage);
}
_currentImage = img;
img.set({ 'left': oldLeft || 0 })
img.set({ 'top': oldTop || 0 })
if (oldHeight && oldHeight > img.getScaledHeight()){
img.scaleToWidth(oldWidth);
img.set({'height': oldHeight })
} else if (oldWidth > img.getScaledWidth()){
img.scaleToHeight(oldHeight);
img.set({'width': oldWidth })
}
img.selectable = true
canvas.add(img).setActiveObject(img);
});
}
function addImage(url){
setProductImage(url)
canvas.renderAll()
}
You can see that the image does scale the way I'd like but it doesn't then align itself.
I'm toying with the idea of dropping a bounding box around the image too and possibly trying to align and scale within that but I'm not sure this is even possible with fabric?
This is what I had done.
When you run scaleToWidth and scaleToHeight the scaleX andscaleY is changing and you need to adjust the oldHeight and oldWidth to the new img.scaleX/img.scalaY
I also rewrite _renderFill method from fabric.Image.prototype to create that offset effect.
Check here:https://jsfiddle.net/mariusturcu93/co8dg527/37/
Code
<html>
<head>
<title>Fabric kicking</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/2.4.0/fabric.js"></script>
<style>
#stage {
border: solid 1px #333;
}
</style>
</head>
<body>
<button onclick="addImage('https://s.hs-data.com/bilder/spieler/gross/208995.jpg')">Add image</button>
<button onclick="addImage('https://cdn.images.express.co.uk/img/dynamic/67/590x/Mateusz-Klich-883903.jpg')">add another image</button>
<canvas id="stage" width="600" height="600"></canvas>
<script>
var canvas = new fabric.Canvas('stage');
var cw = canvas.getWidth()
var ch = canvas.getHeight()
var _currentImage;
function setProductImage(url){
var oldWidth;
var oldHeight;
var oldLeft;
var oldTop;
fabric.Image.fromURL(url, function(img) {
if (_currentImage) {
oldWidth = _currentImage.getScaledWidth()
oldHeight = _currentImage.getScaledHeight()
oldLeft = _currentImage.left
oldTop = _currentImage.top
canvas.remove(_currentImage);
}
_currentImage = img;
img.set({ 'left': oldLeft || 0 })
img.set({ 'top': oldTop || 0 })
if (oldHeight && oldHeight > img.getScaledHeight()){
img.scaleToWidth(oldWidth);
img.set({'height': oldHeight/img.scaleX})
} else if (oldWidth > img.getScaledWidth()){
img.scaleToHeight(oldHeight);
img.set({'width': oldWidth/img.scaleY })
}
img.selectable = true
canvas.add(img).setActiveObject(img);
});
}
function addImage(url){
setProductImage(url)
canvas.renderAll()
}
</script>
<img src="https://s.hs-data.com/bilder/spieler/gross/208995.jpg" />
<img src="https://cdn.images.express.co.uk/img/dynamic/67/590x/Mateusz-Klich-883903.jpg" />
</body>
</html>
fabric.Image.prototype._renderFill rewrite
fabric.Image.prototype._renderFill= (function(renderFill){
return function(ctx) {
var w = this.width, h = this.height, sW = w * this._filterScalingX, sH = h * this._filterScalingY,
x = -w / 2, y = -h / 2, elementToDraw = this._element;
y = y + Math.abs((this._element.height-h)/2);
x = x + Math.abs((this._element.width-w)/2);
elementToDraw && ctx.drawImage(elementToDraw,
this.cropX * this._filterScalingX,
this.cropY * this._filterScalingY,
sW,
sH,
x, y, w, h);
}
})()
Related
I am using PDF.js to show PDF in browser. PDF.js uses canvas to render PDF. I have js scripts that draws the lines on the canvas when user double clicks on the canvas. It also adds X check mark to remove the already drawn line.
based on my research i cannot simply just remove the line from the canvas because underneath pixels are gone when you draw something on it. To get it working i have to store lines and then clear canvas and re-load canvas and re-draw lines
Issue
I am not able to store canvas and restore canvas. When i click on X i was able to get lines re-drawn but canvas does not get restored. Canvas remains blank
Run the demo in full page
$(function () {
var $canvas = $("#myCanvas");
var canvasEl = $canvas.get(0);
var ctx = canvasEl.getContext("2d");
var lines = [];
var backupCanvas = document.createElement("canvas");
var loadingTask = pdfjsLib.getDocument('https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf');
loadingTask.promise.then(function (doc) {
console.log("This file has " + doc._pdfInfo.numPages + " pages");
doc.getPage(1).then(page => {
var scale = 1;
var viewPort = page.getViewport(scale);
canvasEl.width = viewPort.width;
canvasEl.height = viewPort.height;
canvasEl.style.width = "100%";
canvasEl.style.height = "100%";
var wrapper = document.getElementById("wrapperDiv");
wrapper.style.width = Math.floor(viewPort.width / scale) + 'px';
wrapper.style.height = Math.floor(viewPort.height / scale) + 'px';
page.render({
canvasContext: ctx,
viewport: viewPort
});
storeCanvas();
});
});
function storeCanvas() {
backupCanvas.width = canvasEl.width;
backupCanvas.height = canvasEl.height;
backupCanvas.ctx = backupCanvas.getContext("2d");
backupCanvas.ctx.drawImage(canvasEl, 0, 0);
}
function restoreCanvas() {
ctx.drawImage(backupCanvas, 0, 0);
}
$canvas.dblclick(function (e) {
var mousePos = getMousePos(canvasEl, e);
var line = { startX: 0, startY: mousePos.Y, endX: canvasEl.width, endY: mousePos.Y, pageY: e.pageY };
lines.push(line);
drawLine(line, lines.length - 1);
});
function drawLine(line, index) {
// draw line
ctx.beginPath();
ctx.strokeStyle = '#df4b26';
ctx.moveTo(line.startX, line.startY);
ctx.lineTo(line.endX, line.endY);
ctx.closePath();
ctx.stroke();
// add remove mark
var top = line.pageY;
var left = canvasEl.width + 20;
var $a = $("<a href='#' class='w-remove-line'>")
.data("line-index", index)
.attr("style", "line-height:0")
.css({ top: top, left: left, position: 'absolute' })
.html("x")
.click(function () {
var index = $(this).data("line-index");
$(".w-remove-line").remove();
ctx.clearRect(0, 0, canvasEl.width, canvasEl.height);
// restore canvas
restoreCanvas();
lines.splice(index, 1);
for (var i = 0; i < lines.length; i++) {
drawLine(lines[i], i);
}
});
$("body").append($a);
}
function getMousePos(canvas, evt) {
var rect = canvas.getBoundingClientRect();
return {
X: Math.floor(evt.clientX - rect.left),
Y: Math.floor(evt.clientY - rect.top),
};
}
});
canvas {
border: 1px solid red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.2.228/pdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<b> Double Click on PDF to draw line and then click on X to remove lines</b>
<div id="wrapperDiv">
<canvas id="myCanvas"></canvas>
</div>
The PDF.js render() function is async so you need to store the canvas after the render has finished. Your code is firing storeCanvas() too early and storing a blank canvas. Easy fix, render() returns a promise so ...
page.render({
canvasContext: ctx,
viewport: viewPort
}).then( () => {
storeCanvas();
});
https://jsfiddle.net/fyLant01/1/
Reference: from https://github.com/mozilla/pdf.js/blob/master/src/display/api.js#L998
/**
* Begins the process of rendering a page to the desired context.
* #param {RenderParameters} params Page render parameters.
* #return {RenderTask} An object that contains the promise, which
* is resolved when the page finishes rendering.
*/
In my very simple code example below, I am trying to draw a box, 10 pixels in from the margin all the way around the outside of a canvas. Despite getting what seem like legit values from the screen extends of the browser window, I am only getting an L-shaped line, which seems really odd. My code seems very straight forward. So 2 questions:
Why is it so small (eg not going across the whole browser window?
Why isn't it a box like the code suggests it should be?
<html>
<head></head>
<body>
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width=100% height=100%></canvas>
<script>
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - margin;
var endy = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
</script>
</body>
</html>
Here's the result which you can clearly see is both way too small and not a box at all:
Your problem stems from the common misunderstanding of the canvas' height and width properties. You're giving the values as percents, but that wont work. From MDN:
HTMLCanvasElement.height Is a positive integer reflecting the height
HTML attribute of the element interpreted in CSS pixels. When
the attribute is not specified, or if it is set to an invalid value,
like a negative, the default value of 150 is used.
So you should use plain integers instead. If you need a dynamic size, you can use javascript to update the values.
<html>
<head></head>
<body>
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width="628" height="200"></canvas>
<script>
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - margin;
var endy = (window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
</script>
</body>
</html>
Because you are using window / document width & height,
e.g. the entire webpage, so the lines goes outside of the canvas.
you should instead use the canvas element's width & height.
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
// Always check for properties and methods, to make sure your code doesn't break in other browsers.
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = canvas.clientWidth - margin;
var endy = canvas.clientHeight - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<canvas id="DemoCanvas" width=100 height=100></canvas>
To achieve "fullscreen", something you can do is to use a parent element or the body tag and then "copy" it's width & height to the canvas afterwards.
Note that i use vh & vw instead of % to avoid any stretching issues, etc.
var canvas = document.getElementById('DemoCanvas');
var xoutput = document.getElementById('X');
var youtput = document.getElementById('Y');
updateCanvasSize();
function updateCanvasSize(){
canvas.width = bodySim.clientWidth;
canvas.height = bodySim.clientHeight;
draw();
}
function draw(){
if (canvas.getContext)
{
var margin = 10;
var startx = margin;
var starty = margin;
var endx = canvas.clientWidth - margin;
var endy = canvas.clientHeight - margin;
xoutput.innerHTML = endx;
youtput.innerHTML = endy-margin;
var context = canvas.getContext('2d');
context.beginPath();
context.moveTo(startx,starty);
context.lineTo(endx,starty);
context.lineTo(endx,endy);
context.lineTo(startx,endy);
context.lineTo(startx,starty);
context.stroke();
}
} //end of draw
#bodySim {
width: 100vw;
height: 100vh;
}
<p id="X">X:</p>
<p id="Y">Y:</p>
<!--<form>
Timeline Item: <input type="text" name="item"><br>
Timeline Date: <input type="date" name="date"><br>
</form>-->
<div id="bodySim">
<canvas id="DemoCanvas" ></canvas>
</div>
I gone through documentation of cropper by fengyuanchen. I want the image to be fit by default into canvas if rotated. But I couldnt find a way to achieve this. Any idea how to achieve this functionality?
I want it to be like this to be default: link
Check issue demo here: link
I fixed this behavior but for my special needs. I just needed one rotate button which rotates an image in 90° steps. For other purposes you might extend/change my fix.
It works in "strict" mode by dynamically change the cropbox dimensions.
Here my function which is called, when I want to rotate an image. Ah and additionally the misplacement bug has also been fixed.
var $image;
function initCropper() {
$image = $('.imageUploadPreviewWrap > img').cropper({
autoCrop : true,
strict: true,
background: true,
autoCropArea: 1,
crop: function(e) {
}
});
}
function rotateImage() {
//get data
var data = $image.cropper('getCropBoxData');
var contData = $image.cropper('getContainerData');
var imageData = $image.cropper('getImageData');
//set data of cropbox to avoid unwanted behavior due to strict mode
data.width = 2;
data.height = 2;
data.top = 0;
var leftNew = (contData.width / 2) - 1;
data.left = leftNew;
$image.cropper('setCropBoxData',data);
//rotate
$image.cropper('rotate', 90);
//get canvas data
var canvData = $image.cropper('getCanvasData');
//calculate new height and width based on the container dimensions
var heightOld = canvData.height;
var heightNew = contData.height;
var koef = heightNew / heightOld;
var widthNew = canvData.width * koef;
canvData.height = heightNew;
canvData.width = widthNew;
canvData.top = 0;
if (canvData.width >= contData.width) {
canvData.left = 0;
}
else {
canvData.left = (contData.width - canvData.width) / 2;
}
$image.cropper('setCanvasData', canvData);
//and now set cropper "back" to full crop
data.left = 0;
data.top = 0;
data.width = canvData.width;
data.height = canvData.height;
$image.cropper('setCropBoxData',data);
}
This is my extended code provided by AlexanderZ to avoid cuttong wider images than container :)
var contData = $image.cropper('getContainerData');
$image.cropper('setCropBoxData',{
width: 2, height: 2, top: (contData.height/ 2) - 1, left: (contData.width / 2) - 1
});
$image.cropper('rotate', 90);
var canvData = $image.cropper('getCanvasData');
var newWidth = canvData.width * (contData.height / canvData.height);
if (newWidth >= contData.width) {
var newHeight = canvData.height * (contData.width / canvData.width);
var newCanvData = {
height: newHeight,
width: contData.width,
top: (contData.height - newHeight) / 2,
left: 0
};
} else {
var newCanvData = {
height: contData.height,
width: newWidth,
top: 0,
left: (contData.width - newWidth) / 2
};
}
$image.cropper('setCanvasData', newCanvData);
$image.cropper('setCropBoxData', newCanvData);
Not a direct answer to the question ... but i'm betting many people that use this plugin will find this helpfull..
Made this after picking up #AlexanderZ code to rotate the image.
So ... If you guys want to ROTATE or FLIP a image that has already a crop box defined and if you want that cropbox to rotate or flip with the image ... use these functions:
function flipImage(r, data) {
var old_cbox = $image.cropper('getCropBoxData');
var new_cbox = $image.cropper('getCropBoxData');
var canv = $image.cropper('getCanvasData');
if (data.method == "scaleX") {
if (old_cbox.left == canv.left) {
new_cbox.left = canv.left + canv.width - old_cbox.width;
} else {
new_cbox.left = 2 * canv.left + canv.width - old_cbox.left - old_cbox.width;
}
} else {
new_cbox.top = canv.height - old_cbox.top - old_cbox.height;
}
$image.cropper('setCropBoxData', new_cbox);
/* BUG: When rotated to a perpendicular position of the original position , the user perceived axis are now inverted.
Try it yourself: GO to the demo page, rotate 90 degrees then try to flip X axis, you'll notice the image flippped vertically ... but still ... it fliped in relation to its original axis*/
if ( r == 90 || r == 270 || r == -90 || r == -270 ) {
if ( data.method == "scaleX") {
$image.cropper("scaleY", data.option);
} else {
$image.cropper("scaleX", data.option);
}
} else {
$image.cropper(data.method, data.option);
}
$image.cropper(data.method, data.option);
}
function rotateImage(rotate) {
/* var img = $image.cropper('getImageData'); */
var old_cbox = $image.cropper('getCropBoxData');
var new_cbox = $image.cropper('getCropBoxData');
var old_canv = $image.cropper('getCanvasData');
var old_cont = $image.cropper('getContainerData');
$image.cropper('rotate', rotate);
var new_canv = $image.cropper('getCanvasData');
//calculate new height and width based on the container dimensions
var heightOld = new_canv.height;
var widthOld = new_canv.width;
var heightNew = old_cont.height;
var racio = heightNew / heightOld;
var widthNew = new_canv.width * racio;
new_canv.height = Math.round(heightNew);
new_canv.width = Math.round(widthNew);
new_canv.top = 0;
if (new_canv.width >= old_cont.width) {
new_canv.left = 0;
} else {
new_canv.left = Math.round((old_cont.width - new_canv.width) / 2);
}
$image.cropper('setCanvasData', new_canv);
if (rotate == 90) {
new_cbox.height = racio * old_cbox.width;
new_cbox.width = racio * old_cbox.height;
new_cbox.top = new_canv.top + racio * (old_cbox.left - old_canv.left);
new_cbox.left = new_canv.left + racio * (old_canv.height - old_cbox.height - old_cbox.top);
}
new_cbox.width = Math.round(new_cbox.width);
new_cbox.height = Math.round(new_cbox.height);
new_cbox.top = Math.round(new_cbox.top);
new_cbox.left = Math.round(new_cbox.left);
$image.cropper('setCropBoxData', new_cbox);
}
var photoToEdit = $('.photo_container img');
$( photoToEdit ).cropper({
autoCrop : true,
crop: function(e) {}
});
$("#rotate_left_btn").click( function () {
$( photoToEdit ).cropper('rotate', -90);
var containerHeightFactor = $(".photo_container").height() / $( photoToEdit).cropper('getCanvasData').height;
if ( containerHeightFactor < 1 ) { // if canvas height is greater than the photo container height, then scale (on both x and y
// axes to maintain aspect ratio) to make canvas height fit container height
$( photoToEdit).cropper('scale', containerHeightFactor, containerHeightFactor);
} else if ( $( photoToEdit).cropper('getData').scaleX != 1 || $( photoToEdit).cropper('getData').scaleY != 1 ) { // if canvas height
// is NOT greater than container height but image is already scaled, then revert the scaling cuz the current rotation will bring
// the image back to its original orientation (landscape/portrait)
$( photoToEdit).cropper('scale', 1, 1);
}
}
I Fixed this issue hope fully. i have added or changed the option to 0 (viewMode: 0,). Now its working well.
cropper = new Cropper(image, {
dragMode: 'none',
viewMode: 0,
width: 400,
height: 500,
zoomable: true,
rotatable: true,
crop: function(e) {
}
});
document.getElementById('rotateImg').addEventListener('click', function () {
cropper.rotate(90);
});
I have this jsfiddle : http://jsfiddle.net/seekpunk/JDU9f/1/
what I want is when the user do a selection the selected part is zoomed in .Is there anyway to achieve this ?
this is the code so far :
var canvas = document.getElementById("MyCanvas");
var ctx = canvas.getContext('2d'),
w = canvas.width,
h = canvas.height,
x1,
y1,
isDown = false;
var img = new Image();
img.src = "http://www.stockfreeimages.com/static/homepage/female-chaffinch-free-stock-photo-106202.jpg";
canvas.onmousedown = function (e) {
var rect = canvas.getBoundingClientRect();
x1 = e.clientX - rect.left;
y1 = e.clientY - rect.top;
isDown = true;
}
canvas.onmouseup = function () {
isDown = false;
}
canvas.onmousemove = function (e) {
if (!isDown) return;
var rect = canvas.getBoundingClientRect(),
x2 = e.clientX - rect.left,
y2 = e.clientY - rect.top;
ctx.clearRect(0, 0, w, h);
ctx.drawImage(img, 0, 0, w, h);
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
}
img.onload = function () {
ctx.drawImage(img, 0, 0, w, h);
};
I can make the selection but how can i zoom the selected part only ? like this example : http://canvasjs.com/docs/charts/basics-of-creating-html5-chart/zooming-panning/
For those looking for zooming functionality without the use of a canvas, here's a pretty foolproof solution using a simple <img />:
$(window).on("load",function() {
//VARS===================================================
var zoom = {
zoomboxLeft:null, zoomboxTop:null, //zoombox
cursorStartX:null, cursorStartY:null, //cursor
imgStartLeft:null, imgStartTop:null, //image
minDragLeft:null,maxDragLeft:null, minDragTop:null,maxDragTop:null
};
//KEY-HANDLERS===========================================
$(document).keydown(function(e) {
if (e.which==32) {e.preventDefault(); if (!$(".zoombox img").hasClass("moving")) {$(".zoombox img").addClass("drag");}} //SPACE
});
$(document).keyup(function(e) {
if (e.which==32) {if (!$(".zoombox img").hasClass("moving")) {$(".zoombox img").removeClass("drag");}} //SPACE
});
//RESET IMAGE SIZE=======================================
$(".reset").on("click",function() {
var zoombox = "#"+$(this).parent().attr("id")+" .zoombox";
$(zoombox+" img").css({"left":0, "top":0, "width":$(zoombox).width(), "height":$(zoombox).height()});
}).click();
//ZOOM&DRAG-EVENTS=======================================
//MOUSEDOWN----------------------------------------------
$(".zoombox img").mousedown(function(e) {
e.preventDefault();
$(".zoombox img").addClass("moving");
var selector = $(this).next();
var zoombox = $(this).parent();
$(zoombox).addClass("active");
//store zoombox left&top
zoom.zoomboxLeft = $(zoombox).offset().left + parseInt($(zoombox).css("border-left-width").replace(/\D+/,""));
zoom.zoomboxTop = $(zoombox).offset().top + parseInt($(zoombox).css("border-top-width").replace(/\D+/,""));
//store starting positions of cursor (relative to zoombox)
zoom.cursorStartX = e.pageX - zoom.zoomboxLeft;
zoom.cursorStartY = e.pageY - zoom.zoomboxTop;
if ($(".zoombox img").hasClass("drag")) {
//store starting positions of image (relative to zoombox)
zoom.imgStartLeft = $(this).position().left;
zoom.imgStartTop = $(this).position().top;
//set drag boundaries (relative to zoombox)
zoom.minDragLeft = $(zoombox).width() - $(this).width();
zoom.maxDragLeft = 0;
zoom.minDragTop = $(zoombox).height() - $(this).height();
zoom.maxDragTop = 0;
} else {
//set drag boundaries (relative to zoombox)
zoom.minDragLeft = 0;
zoom.maxDragLeft = $(zoombox).width();
zoom.minDragTop = 0;
zoom.maxDragTop = $(zoombox).height();
//activate zoom-selector
$(selector).css({"display":"block", "width":0, "height":0, "left":zoom.cursorStartX, "top":zoom.cursorStartY});
}
});
//MOUSEMOVE----------------------------------------------
$(document).mousemove(function(e) {
if ($(".zoombox img").hasClass("moving")) {
if ($(".zoombox img").hasClass("drag")) {
var img = $(".zoombox.active img")[0];
//update image position (relative to zoombox)
$(img).css({
"left": zoom.imgStartLeft + (e.pageX-zoom.zoomboxLeft)-zoom.cursorStartX,
"top": zoom.imgStartTop + (e.pageY-zoom.zoomboxTop)-zoom.cursorStartY
});
//prevent dragging in prohibited areas (relative to zoombox)
if ($(img).position().left <= zoom.minDragLeft) {$(img).css("left",zoom.minDragLeft);} else
if ($(img).position().left >= zoom.maxDragLeft) {$(img).css("left",zoom.maxDragLeft);}
if ($(img).position().top <= zoom.minDragTop) {$(img).css("top",zoom.minDragTop);} else
if ($(img).position().top >= zoom.maxDragTop) {$(img).css("top",zoom.maxDragTop);}
} else {
//calculate selector width and height (relative to zoombox)
var width = (e.pageX-zoom.zoomboxLeft)-zoom.cursorStartX;
var height = (e.pageY-zoom.zoomboxTop)-zoom.cursorStartY;
//prevent dragging in prohibited areas (relative to zoombox)
if (e.pageX-zoom.zoomboxLeft <= zoom.minDragLeft) {width = zoom.minDragLeft - zoom.cursorStartX;} else
if (e.pageX-zoom.zoomboxLeft >= zoom.maxDragLeft) {width = zoom.maxDragLeft - zoom.cursorStartX;}
if (e.pageY-zoom.zoomboxTop <= zoom.minDragTop) {height = zoom.minDragTop - zoom.cursorStartY;} else
if (e.pageY-zoom.zoomboxTop >= zoom.maxDragTop) {height = zoom.maxDragTop - zoom.cursorStartY;}
//update zoom-selector
var selector = $(".zoombox.active .selector")[0];
$(selector).css({"width":Math.abs(width), "height":Math.abs(height)});
if (width<0) {$(selector).css("left",zoom.cursorStartX-Math.abs(width));}
if (height<0) {$(selector).css("top",zoom.cursorStartY-Math.abs(height));}
}
}
});
//MOUSEUP------------------------------------------------
$(document).mouseup(function() {
if ($(".zoombox img").hasClass("moving")) {
if (!$(".zoombox img").hasClass("drag")) {
var img = $(".zoombox.active img")[0];
var selector = $(".zoombox.active .selector")[0];
if ($(selector).width()>0 && $(selector).height()>0) {
//resize zoom-selector and image
var magnification = ($(selector).width()<$(selector).height() ? $(selector).parent().width()/$(selector).width() : $(selector).parent().height()/$(selector).height()); //go for the highest magnification
var hFactor = $(img).width() / ($(selector).position().left-$(img).position().left);
var vFactor = $(img).height() / ($(selector).position().top-$(img).position().top);
$(selector).css({"width":$(selector).width()*magnification, "height":$(selector).height()*magnification});
$(img).css({"width":$(img).width()*magnification, "height":$(img).height()*magnification});
//correct for misalignment during magnification, caused by size-factor
$(img).css({
"left": $(selector).position().left - ($(img).width()/hFactor),
"top": $(selector).position().top - ($(img).height()/vFactor)
});
//reposition zoom-selector and image (relative to zoombox)
var selectorLeft = ($(selector).parent().width()/2) - ($(selector).width()/2);
var selectorTop = ($(selector).parent().height()/2) - ($(selector).height()/2);
var selectorDeltaLeft = selectorLeft - $(selector).position().left;
var selectorDeltaTop = selectorTop - $(selector).position().top;
$(selector).css({"left":selectorLeft, "top":selectorTop});
$(img).css({"left":"+="+selectorDeltaLeft, "top":"+="+selectorDeltaTop});
}
//deactivate zoom-selector
$(selector).css({"display":"none", "width":0, "height":0, "left":0, "top":0});
} else {$(".zoombox img").removeClass("drag");}
$(".zoombox img").removeClass("moving");
$(".zoombox.active").removeClass("active");
}
});
});
/*CONTAINER-------------------*/
#container {
width: 234px;
height: 199px;
margin-left: auto;
margin-right: auto;
}
/*ZOOMBOX=====================*/
/*IMAGE-----------------------*/
.zoombox {
position:relative;
width: 100%;
height: 100%;
border: 2px solid #AAAAAA;
background-color: #666666;
overflow: hidden;
}
.zoombox img {position:relative;}
.zoombox img.drag {cursor:move;}
.zoombox .selector {
display: none;
position: absolute;
border: 1px solid #999999;
background-color: rgba(255,255,255, 0.3);
}
/*CONTROL---------------------*/
.reset {float:left;}
.info {float:right;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<!--ZOOMBOX-->
<div class="zoombox">
<img src="http://www.w3schools.com/colors/img_colormap.gif" />
<div class="selector"></div>
</div>
<input type="button" class="reset" value="Reset" /><span class="info">Press SPACE to drag</span>
</div>
jsfiddle: http://jsfiddle.net/2nt8k8e6/
The container element can be anything, just place the .zoombox on your webpage and it should work.
You should even be able to put multiple .zoomboxes next to each other in the same container-element. I haven't tested it though.
It's not too complicated, there's not a whole lot of comments, but the variable names are pretty self-explanatory and make the code easy enough to read, and all the jquery-functions can be looked up.
The code is essentially divided into three handlers: .mousedown(), .mousemove(), .mouseup().
- In mousedown some values are stored into variables (like the start-coordinates for the selector), which are used in mousemove and mouseup as a reference.
If you want to know exactly what's happening, just have a look at the code, as I said it's not too difficult.
I'm trying to draw an image to the html5 canvas, but can't quite remember exactly how it's done.
I have the following html document:
<html>
<head>
<title>Understanding Business</title>
<section hidden>
<img id = "startButton" src = "startButton.png" alt = "Start Button" height = "40" width = "50" href = "javascript:drawFirstGameElements();">
</section>
</head>
<body onLoad = "drawStartButton()">
<h1>Understanding Business</h1>
<canvas id ="gameCanvas" width = "1000" height= "500" style = "border:1px solid #000000;">Your browser does not support the HTML5 canvas.</canvas>
<script type = "text/javascript">
window.onload = function(){
var gameCanvas = document.getElementById("gameCanvas");
var context = gameCanvas.getContext("2d");
//gameCanvas.addEventListener('onclick', clickReporter, false);
gameCanvas.addEventListener('mouseMove', function(event){
var mousePosition = getMouseCoords(gameCanvas, event);
});
/* Add some global variables */
var image = new Image();
var imageSource;
var imageX;
var imageY;
function drawStartButton(){
/*image.onload = function(){
context.drawImage(image, 500, 250);
};
image.src = "startButton.png"; */
var startButtonImg = document.getElementById("startButton");
context.drawImage(startButtonImg, 500, 250);
}
function drawFirstGameElements(){
drawDescriptionBox1;
}
function drawDescriptionBox1(){
context.moveTo(100, 400);
context.lineTo(150, 400);
context.lineTo(150, 450);
context.lineTo(100, 450);
context.lineTo(100, 400);
context.moveTo(110, 425);
context.strokeText("Asset");
}
//Function to get mouse coordinates on canvas
function getMouseCoords(gameCanvas, event){
var rect = gameCanvas.getBoundingClientRect(), root = document.documentElement;
//return mouse position
var mouseX = event.clientX - rect.top - root.scrollTop;
var mouseY = event.clientY - rect.left - root.scrollLeft;
return{
x: mouseX,
y: mouseY
};
}
}
For some reason, when I view the page in the browser, although the canvas is displayed, the "start button image" is not displayed on the canvas. It's been a while since I last used the HTML5 canvas, and I can't quite figure out what it is I'm doing wrong... Can someone point me in the right direction?
Cheers,
Edit
Code updated to reflect changes as suggested, but I'm still having the same problem. Any other suggestions?
Typo in your code :
var gameCanvas = document.getElementById("gameCanvas");
var context = gameCanvas.getContext("2d");
//gameCanvas.addEventListener('onclick', clickReporter, false);
gameCanvas.addEventListener('mouseMove', function(event){
var mousePosition = getMouseCoords(gameCanvas, event);
}); // HERE ADD );
/* Add some global variables */
var image = new Image();
var imageSource;
var imageX;
var imageY;
//HERE DELETE THE }
jsfiddle : http://jsfiddle.net/RqbPn/