我在html5中制作了一个画布。我用它来显示你可以移动的更大图像的部分,我想让它可以放大和缩小。但我不知道如何从一定程度上使图像缩放。当我增加图像的大小时,画布显示的部分被移动,我希望画布中心的部分在缩放完成时成为焦点,但无论我如何尝试它都会被扭曲一些怎么样。似乎取决于图像的哪个部分被显示,当缩放发生时,图像的一部分将被移动到不同的坐标。我不知道我将使用什么类型的算法,因此计算图像的移动。
这是我的意思是https://imgur.com/a/aZiVM移动的视觉示例的链接,两个图像的缩放程度相同,但取决于画布中可见的图像部分,图像需要的数量移动缩放不同。
这是我的代码,但它并没有真正起作用。
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"></script>
<style>
body {
padding: 0px;
margin: 0px;
}
</style>
</head>
<body>
<div id="map" style="position:absolute;top:10px;left:50px;">
<canvas id="canvas" width="800" height="600" style="float:left;border:1px solid #000000;">Your browser doesn't support canvas</canvas>
<div id="floorDown" onMouseDown="zoomIn()" style="width:200px;height:50px;float:left;">Zoom in</div><br>
<div id="floorDown" onMouseDown="zoomOut()" style="width:200px;height:50px;float:left;">Zoom out</div>
</div>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var startX;
var startY;
var isDown=false;
//start position
var imageX=0;
var imageY=0;
var imageWidth,imageHeight,imageRight,imageBottom;
var draggingImage=false;
var startX;
var startY;
var img=new Image();
img.onload=function(){
imageWidth=img.width;
imageHeight=img.height;
draw();
}
img.src='http://orig00.deviantart.net/35cb/f/2013/030/f/0/tripolar_by_zy0rg-d5t9tqh.png';
function draw(){
// clear the canvas
ctx.clearRect(0,0,canvas.width,canvas.height);
//Disable anti-aliasing
ctx.imageSmoothingEnabled=false;
// draw the image
ctx.drawImage(img,0,0,img.width,img.height,imageX,imageY,imageWidth,imageHeight);
}
function handleMouseDown(e){
startX=parseInt(e.clientX-offsetX);
startY=parseInt(e.clientY-offsetY);
draggingImage= true;
}
function handleMouseUp(e){
draggingImage=false;
draw();
}
function handleMouseOut(e){
handleMouseUp(e);
}
function handleMouseMove(e){
if(draggingImage){
imageClick=false;
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// move the image by the amount of the latest drag
var dx=mouseX-startX;
var dy=mouseY-startY;
imageX+=dx;
imageY+=dy;
// reset the startXY for next time
startX=mouseX;
startY=mouseY;
// redraw the image with border
draw();
}
}
// TEST zoom in/out functions
function zoomIn() {
imageX=imageX*2;
imageY=imageY*2;
imageWidth=imageWidth*2;
imageHeight=imageHeight*2;
draw();
}
function zoomOut() {
imageX=imageX/2;
imageY=imageY/2;
imageWidth=imageWidth/2;
imageHeight=imageHeight/2;
draw();
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
</script>
</body>
答案 0 :(得分:3)
给定原点(pos)和缩放比例
var pos = {x : 0, y : 0};
var scale = 1;
function zoomAt(x,y,_scale)
scale *= _scale
pos.x = x - (x - pos.x) * scale;
pos.y = y - (y - pos.y) * scale;
}
然后,您可以使用
创建转换ctx.setTransform(scale, 0, 0, scale, pos.x, pos.y);
所以放大屏幕中心
zoomAt(canvas.width / 2, canvas.height / 2, 1.1); // zoom in
zoomAt(canvas.width / 2, canvas.height / 2, 1 / 1.1); // zoom out
全部放在一起
// the following globals are available
// w, h, cw, ch, width height centerWidth centerHeight of canvas
// canvas, ctx, mouse, globalTime
const image = new Image;
image.src = "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/KTZ_2TE10U_Aynabulak.jpg/800px-KTZ_2TE10U_Aynabulak.jpg";
const font = {
font : "28px Arial",
textAlign : "center",
textBaseline : "middle",
}
function setStyle(ctx, style){
Object.keys(style).forEach(key => ctx[key] = style[key]);
}
// Handle all key input
const keys = { // key input object
ArrowLeft : false, // only add key names you want to listen to
ArrowRight : false,
keyEvent (event) {
if (keys[event.code] !== undefined) { // are we interested in this key
keys[event.code] = event.type === "keydown";
}
}
}
// add key listeners
document.addEventListener("keydown", keys.keyEvent);
document.addEventListener("keyup", keys.keyEvent);
const view = (()=>{
const matrix = [1,0,0,1,0,0]; // current view transform
const invMatrix = [1,0,0,1,0,0]; // current inverse view transform
var m = matrix; // alias
var im = invMatrix; // alias
var rotate = 0; // current x axis direction in radians
var scale = 1; // current scale
const pos = { // current position of origin
x : 0,
y : 0,
}
var dirty = true;
return {
apply(ctx){
if(dirty){ this.update() }
var m = matrix;
ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
},
update(){ // call to update transforms
var xdx = Math.cos(rotate) * scale;
var xdy = Math.sin(rotate) * scale;
m[0] = xdx;
m[1] = xdy;
m[2] = -xdy;
m[3] = xdx;
m[4] = pos.x;
m[5] = pos.y;
// calculate the inverse transformation
cross = m[0] * m[3] - m[1] * m[2];
im[0] = m[3] / cross;
im[1] = -m[1] / cross;
im[2] = -m[2] / cross;
im[3] = m[0] / cross;
dirty = false;
},
toWorld(x,y,point = {}){ // convert screen to world coords
var xx, yy;
if(dirty){ this.update() }
xx = x - matrix[4];
yy = y - matrix[5];
point.x = xx * im[0] + yy * im[2];
point.y = xx * im[1] + yy * im[3];
return point;
},
toScreen(x,y,point = {}){ // convert world coords to coords
if(dirty){ this.update() }
point.x = x * m[0] + y * m[2] + m[4];
point.y = x * m[1] + y * m[3] + m[5];
return point;
},
movePos(x,y){
pos.x += x;
pos.y += y;
dirty = true;
},
setPos(x,y){
pos.x = x;
pos.y = y;
dirty = true;
},
setScale(sc){
scale = sc;
dirty = true;
},
scaleScale(sc){
scale *= sc;
dirty = true;
},
scaleAt(x,y,sc){
if(dirty){ this.update() }
scale *= sc;
pos.x = x - (x - pos.x) * sc;
pos.y = y - (y - pos.y) * sc;
dirty = true;
}
};
})();
function onResize(){
setStyle(ctx,font);
}
const drag = {
dragging : false,
lastX : 0,
lastY : 0,
update(){
var dx,dy;
if(mouse.w){
if(mouse.w < 0){
mouse.w += 10;
view.scaleAt(mouse.x,mouse.y,1/1.02);
if(mouse.w > 0){
mouse.w = 0;
}
} else if(mouse.w > 0){
mouse.w -= 10;
view.scaleAt(mouse.x,mouse.y,1.02);
if(mouse.w < 0){
mouse.w = 0;
}
}
}
if(mouse.buttonRaw){
if(!this.dragging){
this.dragging = true;
this.lastX = mouse.x;
this.lastY = mouse.y;
}else{
if(mouse.buttonRaw & 1){
dx = mouse.x-this.lastX;
dy = mouse.y-this.lastY;
this.lastX = mouse.x;
this.lastY = mouse.y;
view.movePos(dx,dy);
}
}
}else{
if(this.dragging){
this.dragging = false;
}
}
}
}
function display() { // call once per frame
ctx.setTransform(1, 0, 0, 1, 0, 0); // reset transform
ctx.globalAlpha = 1; // reset alpha
ctx.clearRect(0, 0, w, h);
if(keys.ArrowLeft ){ mouse.w += 10 }
if(keys.ArrowRight){ mouse.w -= 10 }
drag.update();
if(image.complete){
view.apply(ctx);
ctx.drawImage(image,0,0);
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillText("Click drag to pan. Wheel to zoom or left/right arrow.",cw,20)
}else{
ctx.fillText("Loading Image...",cw,ch)
}
}
/******************************************************************************
The code from here down is generic full page mouse and canvas boiler plate
code. As I do many examples which all require the same mouse and canvas
functionality I have created this code to keep a consistent interface. The
Code may or may not be part of the answer.
This code may or may not have ES6 only sections so will require a transpiler
such as babel.js to run on legacy browsers.
*****************************************************************************/
// V2.0 ES6 version for Stackoverflow and Groover QuickRun
var w, h, cw, ch, canvas, ctx, mouse, globalTime = 0;
// You can declare onResize (Note the capital R) as a callback that is also
// called once at start up. Warning on first call canvas may not be at full
// size.
;(function(){
const RESIZE_DEBOUNCE_TIME = 100;
var resizeTimeoutHandle;
var firstRun = true;
function createCanvas () {
var c,cs;
cs = (c = document.createElement("canvas")).style;
cs.position = "absolute";
cs.top = cs.left = "0px";
cs.zIndex = 10;
document.body.appendChild(c);
return c;
}
function resizeCanvas () {
if (canvas === undefined) { canvas = createCanvas() }
canvas.width = innerWidth;
canvas.height = innerHeight;
ctx = canvas.getContext("2d");
if (typeof setGlobals === "function") { setGlobals() }
if (typeof onResize === "function") {
clearTimeout(resizeTimeoutHandle);
if (firstRun) { onResize() }
else { resizeTimeoutHandle = setTimeout(onResize, RESIZE_DEBOUNCE_TIME) }
firstRun = false;
}
}
function setGlobals () {
cw = (w = canvas.width) / 2;
ch = (h = canvas.height) / 2;
}
mouse = (function () {
function preventDefault(e) { e.preventDefault() }
var m; // alias for mouse
var mouse = {
x : 0, y : 0, w : 0, // mouse position and wheel
alt : false, shift : false, ctrl : false, // mouse modifiers
buttonRaw : 0,
over : false, // true if mouse over the element
buttonOnMasks : [0b1, 0b10, 0b100], // mouse button on masks
buttonOffMasks : [0b110, 0b101, 0b011], // mouse button off masks
active : false,
bounds : null,
eventNames : "mousemove,mousedown,mouseup,mouseout,mouseover,mousewheel,DOMMouseScroll".split(","),
event(e) {
var t = e.type;
m.bounds = m.element.getBoundingClientRect();
m.x = e.pageX - m.bounds.left - scrollX;
m.y = e.pageY - m.bounds.top - scrollY;
m.alt = e.altKey;
m.shift = e.shiftKey;
m.ctrl = e.ctrlKey;
if (t === "mousedown") { m.buttonRaw |= m.buttonOnMasks[e.which - 1] }
else if (t === "mouseup") { m.buttonRaw &= m.buttonOffMasks[e.which - 1] }
else if (t === "mouseout") { m.over = false }
else if (t === "mouseover") { m.over = true }
else if (t === "mousewheel") {
m.w = e.wheelDelta
e.preventDefault();
}
else if (t === "DOMMouseScroll") {
m.w = -e.detail
e.preventDefault();
}
},
start(element) {
m.element = element === undefined ? document : element;
m.eventNames.forEach(name => document.addEventListener(name, mouse.event) );
document.addEventListener("contextmenu", preventDefault, false);
m.active = true;
},
}
m = mouse;
return mouse;
})();
function update(timer) { // Main update loop
globalTime = timer;
display(); // call demo code
requestAnimationFrame(update)
}
setTimeout(function(){
canvas = createCanvas();
mouse.start(canvas, true);
resizeCanvas();
window.addEventListener("resize", resizeCanvas);
requestAnimationFrame(update);
},0);
})();
/** SimpleFullCanvasMouse.js end **/
#imageCC {
font-family : arial;
font-size : 10px;
position : absolute;
z-index : 100;
bottom : 3px;
right : 10px;
background : rgba(255,255,255,0.7);
}
<div id=imageCC>Image rights.
<a href="https://commons.wikimedia.org/wiki/User:Kabelleger">Kabelleger</a> / David Gubler (<a href="http://www.bahnbilder.ch">http://www.bahnbilder.ch</a>), <a href="https://commons.wikimedia.org/wiki/File:KTZ_2TE10U_Aynabulak.jpg">KTZ 2TE10U Aynabulak</a>, <a href="https://creativecommons.org/licenses/by-sa/3.0/legalcode">CC BY-SA 3.0</a>
</div>