gpt4 book ai didi

javascript - 缩放时如何绑定(bind)图像平移(HTML Canvas)

转载 作者:太空宇宙 更新时间:2023-11-04 14:55:10 25 4
gpt4 key购买 nike

我试图限制界限,但遇到了问题。我正在放大另一个 Canvas 上的图像,然后实现缩放和平移。我的问题(下面的代码)是限制/限制 offsetx/y,这样你就永远不会看到空白;仅部分图像。

请原谅!任何帮助表示赞赏! :P

var zoomIntensity = 0.2;

var canvas = document.getElementById("canvas");
var canvas2 = document.getElementById("canvas2");
var context = canvas.getContext("2d");
var context2 = canvas2.getContext("2d");
var width = 200;
var height = 200;

var scale = 1;
var originx = 0;
var originy = 0;

var offset = {x:0, y:0};

//fill smaller canvas with random pixels
for(var x = 0; x < 100; x++)
for(var y = 0; y < 100; y++)
{
var rando = function(){return Math.floor(Math.random() * 9)};
var val = rando();
context2.fillStyle = "#" + val + val + val;
context2.fillRect(x,y,1,1);
}

//draw the larger canvas
function draw()
{
context.imageSmoothingEnabled = false;

// Clear screen to white.
context.fillStyle = "white";
context.fillRect(originx - offset.x, originy - offset.y, width/scale, height/scale);
context.drawImage(canvas2, 0,0, width, height);
}

// Draw loop at 60FPS.
setInterval(draw, 1000/60);

canvas.onmousewheel = function (event){
event.preventDefault();

// Get mouse offset.
var mousex = event.clientX - canvas.offsetLeft;
var mousey = event.clientY - canvas.offsetTop;

// Normalize wheel to +1 or -1.
var wheel = event.wheelDelta/120;

// Compute zoom factor.
var zoom = Math.exp(wheel*zoomIntensity);

// Translate so the visible origin is at the context's origin.
context.translate(originx - offset.x, originy - offset.y); //offset is panning

//make sure we don't zoom out further than normal scale
var resultingScale = scale * zoom;
if(resultingScale < 1)
zoom = 1/scale;

// Compute the new visible origin. Originally the mouse is at a
// distance mouse/scale from the corner, we want the point under
// the mouse to remain in the same place after the zoom, but this
// is at mouse/new_scale away from the corner. Therefore we need to
// shift the origin (coordinates of the corner) to account for this.
originx -= mousex/(scale*zoom) - mousex/scale;
originy -= mousey/(scale*zoom) - mousey/scale;

// Scale it (centered around the origin due to the trasnslate above).
context.scale(zoom, zoom);

// Offset the visible origin to it's proper position.
context.translate(-originx + offset.x, -originy + offset.y); //offset is panning

// Update scale and others.
scale *= zoom;
}

document.onkeydown = function (evt)
{
var offsetx = 0;
var offsety = 0;

switch(evt.which)
{
case 37: //left
offsetx = 1;
break;
case 38: //up
offsety = 1;
break;
case 39: //right
offsetx = -1
break;
case 40: //down
offsety = -1;
break;
}

offsetx /= scale;
offsety /= scale;

offset.x += offsetx;
offset.y += offsety;

context.translate(offsetx,offsety);
}
<canvas id="canvas" width="200" height="200"></canvas>
<canvas id="canvas2" width="100" height="100"></canvas>

最佳答案

使用变换矩阵来约束 View

要限制位置,您需要将图像的 Angular 坐标转换为屏幕坐标。由于获取转换仍然不是跨浏览器的标准,下面的演示包含转换的副本。

对象 view 包含 Canvas View 。当您使用函数 view.setBounds(top,left,right,bottom); 时, View 将锁定到该区域(您正在查看的图像 0,0,100,100)

比例和位置(原点)将被限制在边界之外或由 view.setContext(context) 设置的 Canvas 上下文的边缘之一。

函数 scaleAt(pos,amount); 将在指定位置(例如鼠标位置)缩放

要设置转换,请使用 view.apply() 这将更新 View 转换并设置上下文转换。

还有一些其他功能可能会很方便,请参阅代码。

演示使用鼠标单击拖动进行平移和滚轮缩放。

Demo 是 OP 示例宽度修改的副本以回答问题。

// use requestAnimationFrame when doing any form of animation via javascript
requestAnimationFrame(draw);

var zoomIntensity = 0.2;

var canvas = document.getElementById("canvas");
var canvas2 = document.getElementById("canvas2");
var context = canvas.getContext("2d");
var context2 = canvas2.getContext("2d");
var width = 200;
var height = 200;
context.font = "24px arial";
context.textAlign = "center";
context.lineJoin = "round"; // to prevent miter spurs on strokeText
//fill smaller canvas with random pixels
for(var x = 0; x < 100; x++){
for(var y = 0; y < 100; y++) {
var rando = function(){return Math.floor(Math.random() * 9)};
var val = rando();
if(x === 0 || y === 0 || x === 99 || y === 99){
context2.fillStyle = "#FF0000";
}else{
context2.fillStyle = "#" + val + val + val;

}
context2.fillRect(x,y,1,1);
}
}

// mouse holds mouse position button state, and if mouse over canvas with overid
var mouse = {
pos : {x : 0, y : 0},
worldPos : {x : 0, y : 0},
posLast : {x : 0, y : 0},
button : false,
overId : "", // id of element mouse is over
dragging : false,
whichWheel : -1, // first wheel event will get the wheel
wheel : 0,
}

// View handles zoom and pan (can also handle rotate but have taken that out as rotate can not be contrained without losing some of the image or seeing some of the background.
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 scale = 1; // current scale
const bounds = {
topLeft : 0,
left : 0,
right : 200,
bottom : 200,
}
var useConstraint = true; // if true then limit pan and zoom to
// keep bounds within the current context

var maxScale = 1;
const workPoint1 = {x :0, y : 0};
const workPoint2 = {x :0, y : 0};
const wp1 = workPoint1; // alias
const wp2 = workPoint2; // alias
var ctx;
const pos = { // current position of origin
x : 0,
y : 0,
}
var dirty = true;
const API = {
canvasDefault () { ctx.setTransform(1,0,0,1,0,0) },
apply(){
if(dirty){ this.update() }
ctx.setTransform(m[0],m[1],m[2],m[3],m[4],m[5]);
},
getScale () { return scale },
getMaxScale () { return maxScale },
matrix, // expose the matrix
invMatrix, // expose the inverse matrix
update(){ // call to update transforms
dirty = false;
m[3] = m[0] = scale;
m[1] = m[2] = 0;
m[4] = pos.x;
m[5] = pos.y;
if(useConstraint){
this.constrain();
}
this.invScale = 1 / scale;
// calculate the inverse transformation
var 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;
},
constrain(){
maxScale = Math.min(
ctx.canvas.width / (bounds.right - bounds.left) ,
ctx.canvas.height / (bounds.bottom - bounds.top)
);
if (scale < maxScale) { m[0] = m[3] = scale = maxScale }
wp1.x = bounds.left;
wp1.y = bounds.top;
this.toScreen(wp1,wp2);
if (wp2.x > 0) { m[4] = pos.x -= wp2.x }
if (wp2.y > 0) { m[5] = pos.y -= wp2.y }
wp1.x = bounds.right;
wp1.y = bounds.bottom;
this.toScreen(wp1,wp2);
if (wp2.x < ctx.canvas.width) { m[4] = (pos.x -= wp2.x - ctx.canvas.width) }
if (wp2.y < ctx.canvas.height) { m[5] = (pos.y -= wp2.y - ctx.canvas.height) }

},
toWorld(from,point = {}){ // convert screen to world coords
var xx, yy;
if(dirty){ this.update() }
xx = from.x - m[4];
yy = from.y - m[5];
point.x = xx * im[0] + yy * im[2];
point.y = xx * im[1] + yy * im[3];
return point;
},
toScreen(from,point = {}){ // convert world coords to screen coords
if(dirty){ this.update() }
point.x = from.x * m[0] + from.y * m[2] + m[4];
point.y = from.x * m[1] + from.y * m[3] + m[5];
return point;
},
scaleAt(at, amount){ // at in screen coords
if(dirty){ this.update() }
scale *= amount;
pos.x = at.x - (at.x - pos.x) * amount;
pos.y = at.y - (at.y - pos.y) * amount;
dirty = true;
},
move(x,y){ // move is in screen coords
pos.x += x;
pos.y += y;
dirty = true;
},
setContext(context){
ctx = context;
dirty = true;
},
setBounds(top,left,right,bottom){
bounds.top = top;
bounds.left = left;
bounds.right = right;
bounds.bottom = bottom;
useConstraint = true;
dirty = true;
}
};
return API;
})();
view.setBounds(0,0,canvas2.width,canvas2.height);
view.setContext(context);



//draw the larger canvas
function draw(){
view.canvasDefault(); // se default transform to clear screen
context.imageSmoothingEnabled = false;
context.fillStyle = "white";
context.fillRect(0, 0, width, height);
view.apply(); // set the current view
context.drawImage(canvas2, 0,0);
view.canvasDefault();
if(view.getScale() === view.getMaxScale()){
context.fillStyle = "black";
context.strokeStyle = "white";
context.lineWidth = 2.5;
context.strokeText("Max scale.",context.canvas.width / 2,24);
context.fillText("Max scale.",context.canvas.width / 2,24);
}
requestAnimationFrame(draw);
if(mouse.overId === "canvas"){
canvas.style.cursor = mouse.button ? "none" : "move";
}else{
canvas.style.cursor = "default";
}
}
// add events to document so that mouse is captured when down on canvas
// This allows the mouseup event to be heard no matter where the mouse has
// moved to.
"mousemove,mousedown,mouseup,mousewheel,wheel,DOMMouseScroll".split(",")
.forEach(eventName=>document.addEventListener(eventName,mouseEvent));

function mouseEvent (event){
mouse.overId = event.target.id;
if(event.target.id === "canvas" || mouse.dragging){ // only interested in canvas mouse events including drag event started on the canvas.

mouse.posLast.x = mouse.pos.x;
mouse.posLast.y = mouse.pos.y;
mouse.pos.x = event.clientX - canvas.offsetLeft;
mouse.pos.y = event.clientY - canvas.offsetTop;
view.toWorld(mouse.pos, mouse.worldPos); // gets the world coords (where on canvas 2 the mouse is)
if (event.type === "mousemove"){
if(mouse.button){
view.move(
mouse.pos.x - mouse.posLast.x,
mouse.pos.y - mouse.posLast.y
)
}
} else if (event.type === "mousedown") { mouse.button = true; mouse.dragging = true }
else if (event.type === "mouseup") { mouse.button = false; mouse.dragging = false }
else if(event.type === "mousewheel" && (mouse.whichWheel === 1 || mouse.whichWheel === -1)){
mouse.whichWheel = 1;
mouse.wheel = event.wheelDelta;
}else if(event.type === "wheel" && (mouse.whichWheel === 2 || mouse.whichWheel === -1)){
mouse.whichWheel = 2;
mouse.wheel = -event.deltaY;
}else if(event.type === "DOMMouseScroll" && (mouse.whichWheel === 3 || mouse.whichWheel === -1)){
mouse.whichWheel = 3;
mouse.wheel = -event.detail;
}
if(mouse.wheel !== 0){
event.preventDefault();
view.scaleAt(mouse.pos, Math.exp((mouse.wheel / 120) *zoomIntensity));
mouse.wheel = 0;
}
}
}
div { user-select: none;}   /* mouse prevent drag selecting content */
canvas { border:2px solid black;}
<div>
<canvas id="canvas" width="200" height="200"></canvas>
<canvas id="canvas2" width="100" height="100"></canvas>
<p>Mouse wheel to zoom. Mouse click drag to pan.</p>
<p>Zoomed image constrained to canvas</p>
</div>

关于javascript - 缩放时如何绑定(bind)图像平移(HTML Canvas),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44009094/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com