Ich möchte in der Lage sein, den Punkt unter der Maus in einer HTML 5-Zeichenfläche zu vergrößern, wie z. B. das Zoomen in Google Maps . Wie kann ich das erreichen?
Ich möchte in der Lage sein, den Punkt unter der Maus in einer HTML 5-Zeichenfläche zu vergrößern, wie z. B. das Zoomen in Google Maps . Wie kann ich das erreichen?
Antworten:
Die bessere Lösung besteht darin, die Position des Ansichtsfensters basierend auf der Änderung des Zooms einfach zu verschieben. Der Zoompunkt ist einfach der Punkt im alten Zoom und im neuen Zoom, den Sie beibehalten möchten. Das heißt, das vorgezoomte Ansichtsfenster und das nachgezoomte Ansichtsfenster haben denselben Zoompunkt relativ zum Ansichtsfenster. Vorausgesetzt, wir skalieren relativ zum Ursprung. Sie können die Position des Ansichtsfensters entsprechend anpassen:
scalechange = newscale - oldscale;
offsetX = -(zoomPointX * scalechange);
offsetY = -(zoomPointY * scalechange);
Sie können also beim Vergrößern einfach nach unten und rechts schwenken, um einen Faktor, um wie viel Sie hineingezoomt haben, relativ zu dem Punkt, auf den Sie gezoomt haben.
Endlich gelöst:
var zoomIntensity = 0.2;
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
var width = 600;
var height = 200;
var scale = 1;
var originx = 0;
var originy = 0;
var visibleWidth = width;
var visibleHeight = height;
function draw(){
// Clear screen to white.
context.fillStyle = "white";
context.fillRect(originx,originy,800/scale,600/scale);
// Draw the black square.
context.fillStyle = "black";
context.fillRect(50,50,100,100);
}
// Draw loop at 60FPS.
setInterval(draw, 1000/60);
canvas.onwheel = 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.deltaY < 0 ? 1 : -1;
// Compute zoom factor.
var zoom = Math.exp(wheel*zoomIntensity);
// Translate so the visible origin is at the context's origin.
context.translate(originx, originy);
// 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, -originy);
// Update scale and others.
scale *= zoom;
visibleWidth = width / scale;
visibleHeight = height / scale;
}
<canvas id="canvas" width="600" height="200"></canvas>
Wie @Tatarize hervorhob , besteht der Schlüssel darin, die Achsenposition so zu berechnen, dass der Zoompunkt (Mauszeiger) nach dem Zoom an derselben Stelle bleibt.
Ursprünglich befindet sich die Maus in einem Abstand mouse/scale
von der Ecke. Wir möchten, dass der Punkt unter der Maus nach dem Zoom an derselben Stelle bleibt, aber dies ist mouse/new_scale
von der Ecke entfernt. Deshalb müssen wir die origin
(Koordinaten der Ecke) verschieben, um dies zu berücksichtigen.
originx -= mousex/(scale*zoom) - mousex/scale;
originy -= mousey/(scale*zoom) - mousey/scale;
scale *= zoom
Der verbleibende Code muss dann die Skalierung anwenden und in den Zeichnungskontext übersetzen, damit sein Ursprung mit der Canvas-Ecke übereinstimmt.
Dies ist tatsächlich ein sehr schwieriges Problem (mathematisch), und ich arbeite fast an der gleichen Sache. Ich habe eine ähnliche Frage zu Stackoverflow gestellt, aber keine Antwort erhalten, aber in DocType (StackOverflow für HTML / CSS) gepostet und eine Antwort erhalten. Überprüfen Sie es heraus http://doctype.com/javascript-image-zoom-css3-transforms-calculate-origin-example
Ich bin gerade dabei, ein jQuery-Plugin zu erstellen, das dies tut (Zoom im Google Maps-Stil mithilfe von CSS3-Transformationen). Ich habe das Zoom-zu-Maus-Cursor-Bit in Ordnung und versuche immer noch herauszufinden, wie der Benutzer die Leinwand wie in Google Maps verschieben kann. Wenn es funktioniert, werde ich hier eine Postleitzahl veröffentlichen, aber den obigen Link für den Maus-Zoom-to-Point-Teil lesen.
Ich wusste nicht, dass es Skalierungs- und Übersetzungsmethoden im Canvas-Kontext gibt. Mit CSS3 können Sie dasselbe erreichen, z. Verwenden von jQuery:
$('div.canvasContainer > canvas')
.css('-moz-transform', 'scale(1) translate(0px, 0px)')
.css('-webkit-transform', 'scale(1) translate(0px, 0px)')
.css('-o-transform', 'scale(1) translate(0px, 0px)')
.css('transform', 'scale(1) translate(0px, 0px)');
Stellen Sie sicher, dass Sie den CSS3-Transformationsursprung auf 0, 0 setzen (-moz-Transformationsursprung: 0 0). Mit der CSS3-Transformation können Sie alles vergrößern. Stellen Sie lediglich sicher, dass der Container-DIV auf Überlauf eingestellt ist: Ausgeblendet, um zu verhindern, dass die gezoomten Kanten aus den Seiten herauslaufen.
Es liegt an Ihnen, ob Sie CSS3-Transformationen oder Canvas-eigene Skalierungs- und Übersetzungsmethoden verwenden. Überprüfen Sie jedoch den obigen Link für die Berechnungen.
Update: Meh! Ich werde nur den Code hier posten, anstatt Sie dazu zu bringen, einem Link zu folgen:
$(document).ready(function()
{
var scale = 1; // scale of the image
var xLast = 0; // last x location on the screen
var yLast = 0; // last y location on the screen
var xImage = 0; // last x location on the image
var yImage = 0; // last y location on the image
// if mousewheel is moved
$("#mosaicContainer").mousewheel(function(e, delta)
{
// find current location on screen
var xScreen = e.pageX - $(this).offset().left;
var yScreen = e.pageY - $(this).offset().top;
// find current location on the image at the current scale
xImage = xImage + ((xScreen - xLast) / scale);
yImage = yImage + ((yScreen - yLast) / scale);
// determine the new scale
if (delta > 0)
{
scale *= 2;
}
else
{
scale /= 2;
}
scale = scale < 1 ? 1 : (scale > 64 ? 64 : scale);
// determine the location on the screen at the new scale
var xNew = (xScreen - xImage) / scale;
var yNew = (yScreen - yImage) / scale;
// save the current screen location
xLast = xScreen;
yLast = yScreen;
// redraw
$(this).find('div').css('-moz-transform', 'scale(' + scale + ')' + 'translate(' + xNew + 'px, ' + yNew + 'px' + ')')
.css('-moz-transform-origin', xImage + 'px ' + yImage + 'px')
return false;
});
});
Sie müssen es natürlich anpassen, um die Canvas-Skala zu verwenden und Methoden zu übersetzen.
Update 2: Ich habe gerade bemerkt, dass ich transform-origin zusammen mit translate verwende. Ich habe es geschafft, eine Version zu implementieren, die nur Skalierung und Übersetzung selbst verwendet. Schauen Sie hier nach: http://www.dominicpettifer.co.uk/Files/Mosaic/MosaicTest.html Warten Sie, bis die Bilder heruntergeladen wurden, und verwenden Sie dann Ihre Mausrad zum Zoomen, unterstützt auch das Schwenken durch Ziehen des Bildes. Es werden CSS3-Transformationen verwendet, aber Sie sollten in der Lage sein, dieselben Berechnungen für Ihre Leinwand zu verwenden.
Ich bin auf dieses Problem mit C ++ gestoßen, was ich wahrscheinlich nicht hätte tun sollen. Ich habe zunächst nur OpenGL-Matrizen verwendet. Wie auch immer, wenn Sie ein Steuerelement verwenden, dessen Ursprung in der oberen linken Ecke liegt, und Sie möchten schwenken / zoomen Hier ist wie bei Google Maps das Layout (mit Allegro als Event-Handler):
// initialize
double originx = 0; // or whatever its base offset is
double originy = 0; // or whatever its base offset is
double zoom = 1;
.
.
.
main(){
// ...set up your window with whatever
// tool you want, load resources, etc
.
.
.
while (running){
/* Pan */
/* Left button scrolls. */
if (mouse == 1) {
// get the translation (in window coordinates)
double scroll_x = event.mouse.dx; // (x2-x1)
double scroll_y = event.mouse.dy; // (y2-y1)
// Translate the origin of the element (in window coordinates)
originx += scroll_x;
originy += scroll_y;
}
/* Zoom */
/* Mouse wheel zooms */
if (event.mouse.dz!=0){
// Get the position of the mouse with respect to
// the origin of the map (or image or whatever).
// Let us call these the map coordinates
double mouse_x = event.mouse.x - originx;
double mouse_y = event.mouse.y - originy;
lastzoom = zoom;
// your zoom function
zoom += event.mouse.dz * 0.3 * zoom;
// Get the position of the mouse
// in map coordinates after scaling
double newx = mouse_x * (zoom/lastzoom);
double newy = mouse_y * (zoom/lastzoom);
// reverse the translation caused by scaling
originx += mouse_x - newx;
originy += mouse_y - newy;
}
}
}
.
.
.
draw(originx,originy,zoom){
// NOTE:The following is pseudocode
// the point is that this method applies so long as
// your object scales around its top-left corner
// when you multiply it by zoom without applying a translation.
// draw your object by first scaling...
object.width = object.width * zoom;
object.height = object.height * zoom;
// then translating...
object.X = originx;
object.Y = originy;
}
Ich mag die Antwort von Tatarize , aber ich werde eine Alternative anbieten . Dies ist ein triviales lineares Algebra-Problem, und die von mir vorgestellte Methode funktioniert gut mit Schwenken, Zoomen, Neigen usw. Das heißt, es funktioniert gut, wenn Ihr Bild bereits transformiert ist.
Wenn eine Matrix skaliert wird, befindet sich die Skalierung am Punkt (0, 0). Wenn Sie also ein Bild haben und es um den Faktor 2 skalieren, verdoppelt sich der Punkt unten rechts sowohl in x- als auch in y-Richtung (unter Verwendung der Konvention, dass [0, 0] oben links im Bild ist).
Wenn Sie stattdessen das Bild um die Mitte zoomen möchten, lautet die Lösung wie folgt: (1) Verschieben Sie das Bild so, dass seine Mitte bei (0, 0) liegt. (2) Skalieren des Bildes um x- und y-Faktoren; (3) Übersetzen Sie das Bild zurück. dh
myMatrix
.translate(image.width / 2, image.height / 2) // 3
.scale(xFactor, yFactor) // 2
.translate(-image.width / 2, -image.height / 2); // 1
Noch abstrakter funktioniert dieselbe Strategie für jeden Punkt. Wenn Sie beispielsweise das Bild an einem Punkt P skalieren möchten:
myMatrix
.translate(P.x, P.y)
.scale(xFactor, yFactor)
.translate(-P.x, -P.y);
Und wenn das Bild bereits auf irgendeine Weise transformiert wurde (z. B. wenn es gedreht, verzerrt, übersetzt oder skaliert wurde), muss die aktuelle Transformation beibehalten werden. Insbesondere muss die oben definierte Transformation mit der aktuellen Transformation nachmultipliziert (oder rechtsmultipliziert) werden.
myMatrix
.translate(P.x, P.y)
.scale(xFactor, yFactor)
.translate(-P.x, -P.y)
.multiply(myMatrix);
Hier hast du es. Hier ist ein Plunk, der dies in Aktion zeigt. Scrollen Sie mit dem Mausrad auf den Punkten und Sie werden sehen, dass sie konstant stehen bleiben. (Nur in Chrome getestet.) Http://plnkr.co/edit/3aqsWHPLlSXJ9JCcJzgH?p=preview
Hier ist meine Lösung für ein zentrumsorientiertes Bild:
var MIN_SCALE = 1;
var MAX_SCALE = 5;
var scale = MIN_SCALE;
var offsetX = 0;
var offsetY = 0;
var $image = $('#myImage');
var $container = $('#container');
var areaWidth = $container.width();
var areaHeight = $container.height();
$container.on('wheel', function(event) {
event.preventDefault();
var clientX = event.originalEvent.pageX - $container.offset().left;
var clientY = event.originalEvent.pageY - $container.offset().top;
var nextScale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale - event.originalEvent.deltaY / 100));
var percentXInCurrentBox = clientX / areaWidth;
var percentYInCurrentBox = clientY / areaHeight;
var currentBoxWidth = areaWidth / scale;
var currentBoxHeight = areaHeight / scale;
var nextBoxWidth = areaWidth / nextScale;
var nextBoxHeight = areaHeight / nextScale;
var deltaX = (nextBoxWidth - currentBoxWidth) * (percentXInCurrentBox - 0.5);
var deltaY = (nextBoxHeight - currentBoxHeight) * (percentYInCurrentBox - 0.5);
var nextOffsetX = offsetX - deltaX;
var nextOffsetY = offsetY - deltaY;
$image.css({
transform : 'scale(' + nextScale + ')',
left : -1 * nextOffsetX * nextScale,
right : nextOffsetX * nextScale,
top : -1 * nextOffsetY * nextScale,
bottom : nextOffsetY * nextScale
});
offsetX = nextOffsetX;
offsetY = nextOffsetY;
scale = nextScale;
});
body {
background-color: orange;
}
#container {
margin: 30px;
width: 500px;
height: 500px;
background-color: white;
position: relative;
overflow: hidden;
}
img {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
max-width: 100%;
max-height: 100%;
margin: auto;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<img id="myImage" src="http://s18.postimg.org/eplac6dbd/mountain.jpg">
</div>
Hier ist eine alternative Methode, bei der setTransform () anstelle von scale () und translate () verwendet wird. Alles ist im selben Objekt gespeichert. Es wird angenommen, dass sich die Zeichenfläche auf der Seite bei 0,0 befindet. Andernfalls müssen Sie ihre Position von den Seitenkoordinaten abziehen.
this.zoomIn = function (pageX, pageY) {
var zoomFactor = 1.1;
this.scale = this.scale * zoomFactor;
this.lastTranslation = {
x: pageX - (pageX - this.lastTranslation.x) * zoomFactor,
y: pageY - (pageY - this.lastTranslation.y) * zoomFactor
};
this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
this.lastTranslation.x,
this.lastTranslation.y);
};
this.zoomOut = function (pageX, pageY) {
var zoomFactor = 1.1;
this.scale = this.scale / zoomFactor;
this.lastTranslation = {
x: pageX - (pageX - this.lastTranslation.x) / zoomFactor,
y: pageY - (pageY - this.lastTranslation.y) / zoomFactor
};
this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
this.lastTranslation.x,
this.lastTranslation.y);
};
Begleitcode für das Schwenken:
this.startPan = function (pageX, pageY) {
this.startTranslation = {
x: pageX - this.lastTranslation.x,
y: pageY - this.lastTranslation.y
};
};
this.continuePan = function (pageX, pageY) {
var newTranslation = {x: pageX - this.startTranslation.x,
y: pageY - this.startTranslation.y};
this.canvasContext.setTransform(this.scale, 0, 0, this.scale,
newTranslation.x, newTranslation.y);
};
this.endPan = function (pageX, pageY) {
this.lastTranslation = {
x: pageX - this.startTranslation.x,
y: pageY - this.startTranslation.y
};
};
Um die Antwort selbst abzuleiten, müssen Sie berücksichtigen, dass dieselben Seitenkoordinaten vor und nach dem Zoom mit denselben Canvas-Koordinaten übereinstimmen müssen. Dann können Sie ausgehend von dieser Gleichung eine Algebra machen:
(pageCoords - Übersetzung) / scale = canvasCoords
Ich möchte hier einige Informationen für diejenigen einfügen, die Bilder separat zeichnen und bewegen - zoomen.
Dies kann nützlich sein, wenn Sie Zooms und die Position des Ansichtsfensters speichern möchten.
Hier ist Schublade:
function redraw_ctx(){
self.ctx.clearRect(0,0,canvas_width, canvas_height)
self.ctx.save()
self.ctx.scale(self.data.zoom, self.data.zoom) //
self.ctx.translate(self.data.position.left, self.data.position.top) // position second
// Here We draw useful scene My task - image:
self.ctx.drawImage(self.img ,0,0) // position 0,0 - we already prepared
self.ctx.restore(); // Restore!!!
}
Hinweis Skala muss zuerst sein .
Und hier ist Zoomer:
function zoom(zf, px, py){
// zf - is a zoom factor, which in my case was one of (0.1, -0.1)
// px, py coordinates - is point within canvas
// eg. px = evt.clientX - canvas.offset().left
// py = evt.clientY - canvas.offset().top
var z = self.data.zoom;
var x = self.data.position.left;
var y = self.data.position.top;
var nz = z + zf; // getting new zoom
var K = (z*z + z*zf) // putting some magic
var nx = x - ( (px*zf) / K );
var ny = y - ( (py*zf) / K);
self.data.position.left = nx; // renew positions
self.data.position.top = ny;
self.data.zoom = nz; // ... and zoom
self.redraw_ctx(); // redraw context
}
und natürlich brauchen wir einen schlepper:
this.my_cont.mousemove(function(evt){
if (is_drag){
var cur_pos = {x: evt.clientX - off.left,
y: evt.clientY - off.top}
var diff = {x: cur_pos.x - old_pos.x,
y: cur_pos.y - old_pos.y}
self.data.position.left += (diff.x / self.data.zoom); // we want to move the point of cursor strictly
self.data.position.top += (diff.y / self.data.zoom);
old_pos = cur_pos;
self.redraw_ctx();
}
})
Hier ist eine Code-Implementierung der Antwort von @ tatarize unter Verwendung von PIXI.js. Ich habe ein Ansichtsfenster, das einen Teil eines sehr großen Bildes betrachtet (z. B. Google Maps-Stil).
$canvasContainer.on('wheel', function (ev) {
var scaleDelta = 0.02;
var currentScale = imageContainer.scale.x;
var nextScale = currentScale + scaleDelta;
var offsetX = -(mousePosOnImage.x * scaleDelta);
var offsetY = -(mousePosOnImage.y * scaleDelta);
imageContainer.position.x += offsetX;
imageContainer.position.y += offsetY;
imageContainer.scale.set(nextScale);
renderer.render(stage);
});
$canvasContainer
ist mein HTML-Container.imageContainer
ist mein PIXI-Container, der das Bild enthält.mousePosOnImage
ist die Mausposition relativ zum gesamten Bild (nicht nur zum Ansichtsfenster).So habe ich die Mausposition erhalten:
imageContainer.on('mousemove', _.bind(function(ev) {
mousePosOnImage = ev.data.getLocalPosition(imageContainer);
mousePosOnViewport.x = ev.data.originalEvent.offsetX;
mousePosOnViewport.y = ev.data.originalEvent.offsetY;
},self));
Sie müssen den Punkt im Weltraum (im Gegensatz zum Bildschirmbereich) vor und nach dem Zoomen ermitteln und dann durch das Delta übersetzen.
mouse_world_position = to_world_position(mouse_screen_position);
zoom();
mouse_world_position_new = to_world_position(mouse_screen_position);
translation += mouse_world_position_new - mouse_world_position;
Die Mausposition befindet sich im Bildschirmbereich, daher müssen Sie sie in den Weltraum umwandeln. Die einfache Transformation sollte folgendermaßen aussehen:
world_position = screen_position / scale - translation
Mit der Funktion scrollto (x, y) können Sie die Position der Bildlaufleiste bis zu dem Punkt behandeln, der nach dem Zoomen angezeigt werden muss. Um die Position der Maus zu ermitteln, verwenden Sie event.clientX und event.clientY. das wird dir helfen
Eine wichtige Sache ... wenn Sie etwas haben wie:
body {
zoom: 0.9;
}
Sie müssen das Äquivalente auf Leinwand machen:
canvas {
zoom: 1.1;
}