Cette question est similaire à celle-ci: Zoom sur un point (avec l’échelle et la translation) Ou même celle-ci: Zoom sur l’image centré sur la position de la souris Mais je ne veux pas pour le faire sur une toile mais une image normale (ou plutôt le conteneur div de l'image) . Donc, le zoom devrait être comme google maps . Je suis en train de pirater/améliorer le zoom iDangerous Swiper ( http://idangero.us/swiper/ ), et c’est mon point de départ. C’est ce que j’ai eu jusqu’à présent: https://jsfiddle.net/xta2ccdt/3/
Zoom uniquement avec la molette de la souris. La première fois que vous effectuez un zoom avant, il effectue un zoom parfaitement, mais je ne vois pas comment calculer chaque zoom après le premier.
Voici mon code: JS:
$(document).ready(function(){
$("#slideContainer").on("mousewheel DOMMouseScroll", function (e) {
e.preventDefault();
var delta = e.delta || e.originalEvent.wheelDelta;
var zoomOut;
if (delta === undefined) {
//we are on firefox
delta = e.originalEvent.detail;
zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
zoomOut = !zoomOut;
} else {
zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
}
var touchX = e.type === 'touchend' ? e.changedTouches[0].pageX : e.pageX;
var touchY = e.type === 'touchend' ? e.changedTouches[0].pageY : e.pageY;
var scale = 1, translateX, translateY;
if(zoomOut){
//we are zooming out
//not interested in this yet
}else{
//we are zooming in
scale = scale + 0.5;
var dimensionMultiplier = scale - 0.5;//when image is scaled up offsetWidth/offsetHeight doesn't take this into account so we must multiply by scale to get the correct width/height
var slideWidth = $("#slide")[0].offsetWidth * dimensionMultiplier;
var slideHeight = $("#slide")[0].offsetHeight * dimensionMultiplier;
var offsetX = $("#slide").offset().left;//distance from the left of the viewport to the slide
var offsetY = $("#slide").offset().top;//distance from the top of the viewport to the slide
var diffX = offsetX + slideWidth / 2 - touchX;//this is distance from the mouse to the center of the image
var diffY = offsetY + slideHeight / 2 - touchY;//this is distance from the mouse to the center of the image
//how much to translate by x and y so that poin on image is alway under the mouse
//we must multiply by 0.5 because the difference between previous and current scale is always 0.5
translateX = ((diffX) * (0.5));
translateY = ((diffY) * (0.5));
}
$("#slide").css("transform", 'translate3d(' + translateX + 'px, ' + translateY + 'px,0) scale(' + scale + ')').css('transition-duration', '300ms');
});
});
HTML:
<div id="slideContainer">
<div id="slide">
<img src="http://content.worldcarfans.co/2008/6/medium/9080606.002.1M.jpg"></img>
</div>
</div>
CSS:
#slideContainer{
width:500px;
height:500px;
overflow:hidden;
}
#slide{
width:100%;
height:100%;
}
img{
width:auto;
height:auto;
max-width:100%;
}
J'ai également compris que si je soustrais les valeurs translateX et translateY précédentes aux valeurs actuelles, je peux zoomer autant que je le souhaite et le zoom sera parfait, mais si je zoome sur un point, puis change la position de la souris et le zoom Encore une fois, le zoom ne sera plus comme il est supposé. Exemple: https://jsfiddle.net/xta2ccdt/4/
Si je change la position de la souris et calcule la différence X et Y entre l'ancienne et la nouvelle position de la souris et que je l'ajoute au calcul du diff, le zoom sera correctement effectué une deuxième fois. Mais la troisième fois, on dirait que la différence est encore soustraite du calcul total, ce qui entraînera le déplacement de l’image par la translation, après quoi, si nous maintenons la souris dans la même position, le zoom sera correctement corrigé à nouveau . pense que je vais juste ajouter la différence entre l'ancienne et la nouvelle position de la souris chaque fois que je calcule le nouveau "diff", et ce genre de travaux, il n'y a plus de saut comme ce fut quand j'ai arrêté d'ajouter la différence de position de la souris ne zoomant toujours pas sur la même position, à chaque nouveau zoom, il déplace (décale) l'image d'une petite quantité. Je suppose que c'est parce qu'il y a une nouvelle valeur de zoom à chaque fois, mais le décalage n'est pas linéaire, il est chaque fois plus petit et s'approche de zéro et je ne peux pas comprendre comment compenser le décalage . Voici le nouvel exemple: https://jsfiddle.net/xta2ccdt/5/ Nouvelle image dans l'exemple: l'ancienne image n'est plus disponible: https://jsfiddle.net/xta2ccdt/14/
Vous en étiez proche, mais il est préférable de stocker séparément les valeurs x, y et scale et de calculer les transformations en fonction de ces valeurs. Cela rend les choses beaucoup plus faciles + économise des ressources (pas besoin de rechercher les propriétés dom encore et encore),
J'ai mis le code dans un module Nice:
function ScrollZoom(container,max_scale,factor){
var target = container.children().first()
var size = {w:target.width(),h:target.height()}
var pos = {x:0,y:0}
var zoom_target = {x:0,y:0}
var zoom_point = {x:0,y:0}
var scale = 1
target.css('transform-Origin','0 0')
target.on("mousewheel DOMMouseScroll",scrolled)
function scrolled(e){
var offset = container.offset()
zoom_point.x = e.pageX - offset.left
zoom_point.y = e.pageY - offset.top
e.preventDefault();
var delta = e.delta || e.originalEvent.wheelDelta;
if (delta === undefined) {
//we are on firefox
delta = e.originalEvent.detail;
}
delta = Math.max(-1,Math.min(1,delta)) // cap the delta to [-1,1] for cross browser consistency
// determine the point on where the slide is zoomed in
zoom_target.x = (zoom_point.x - pos.x)/scale
zoom_target.y = (zoom_point.y - pos.y)/scale
// apply zoom
scale += delta*factor * scale
scale = Math.max(1,Math.min(max_scale,scale))
// calculate x and y based on zoom
pos.x = -zoom_target.x * scale + zoom_point.x
pos.y = -zoom_target.y * scale + zoom_point.y
// Make sure the slide stays in its container area when zooming out
if(pos.x>0)
pos.x = 0
if(pos.x+size.w*scale<size.w)
pos.x = -size.w*(scale-1)
if(pos.y>0)
pos.y = 0
if(pos.y+size.h*scale<size.h)
pos.y = -size.h*(scale-1)
update()
}
function update(){
target.css('transform','translate('+(pos.x)+'px,'+(pos.y)+'px) scale('+scale+','+scale+')')
}
}
Utilisez-le en appelant
new ScrollZoom($('#container'),4,0.5)
Les paramètres sont:
Je pense que cela vous rapprochera un peu plus de ce que vous essayez d’atteindre.
Changements clés
transform-Origin
sur votre souris (à moins que vous ne souhaitiez le garder centré, valeur par défaut).var scale = 1;
$(document).ready(function(){
$("#slideContainer").on("mousewheel DOMMouseScroll", function (e) {
e.preventDefault();
var delta = e.delta || e.originalEvent.wheelDelta;
var zoomOut;
if (delta === undefined) {
//we are on firefox
delta = e.originalEvent.detail;
zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
zoomOut = !zoomOut;
} else {
zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
}
var touchX = e.type === 'touchend' ? e.changedTouches[0].pageX : e.pageX;
var touchY = e.type === 'touchend' ? e.changedTouches[0].pageY : e.pageY;
var translateX, translateY;
if(zoomOut){
// we are zooming out
scale = scale - 0.01;
var offsetWidth = $("#slide")[0].offsetWidth;
var offsetHeight = $("#slide")[0].offsetHeight;
$("#slide")
.css("transform-Origin", touchX + 'px ' + touchY + 'px')
.css("transform", 'scale(' + scale + ')');
}else{
// we are zooming in
scale = scale + 0.01;
var offsetWidth = $("#slide")[0].offsetWidth;
var offsetHeight = $("#slide")[0].offsetHeight;
$("#slide")
.css("transform-Origin", touchX + 'px ' + touchY + 'px')
.css("transform", 'scale(' + scale + ')');
}
});
});
#slideContainer{
width:200px;
height:200px;
overflow:hidden;
}
#slide{
width:100%;
height:100%;
}
img{
width:auto;
height:auto;
max-width:100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="slideContainer">
<div id="slide">
<img src="https://via.placeholder.com/200x200"></img>
</div>
</div>
Le code que j'utilise pour effectuer un zoom avant sur la position de la souris est ci-dessous. Il n'utilise pas transform
/translate3d
mais réajuste la position de l'image dans le div et ajuste ses height
et width
.
var zoom = 1;
var img, div;
window.onload = function() {
window.addEventListener('DOMMouseScroll', wheel, false)
img = document.getElementById("img");
div = document.getElementById("div");
}
function wheel(event) {
event.preventDefault();
var delta = 0;
if (!event) /* For IE. */
event = window.event;
if (event.wheelDelta) { /* IE/Opera. */
delta = event.wheelDelta / 120;
} else if (event.detail) { /** Mozilla case. */
/** In Mozilla, sign of delta is different than in IE.
* Also, delta is multiple of 3.
*/
delta = -event.detail / 3;
}
/** If delta is nonzero, handle it.
* Positive Delta = wheel scrolled up,
* Negative Delte = wheel scrolled down.
*/
if (delta) {
// will pass 1 to zoom in and -1 to zoom out
delta = delta / Math.abs(delta)
zoomImage(delta == 1, event);
}
}
function zoomImage(zoomIn, e) {
var oldZoom = zoom;
var direction = 1 * (zoomIn ? 1 : -1);
zoom += direction * .2;
// range = 50% => 600%
zoom = round(Math.min(6, Math.max(.5, zoom)), 1);
if (zoom == 1) {
// For a zoom = 1, we reset
resetZoom(div, img);
return;
}
// make the position of the mouse the center,
// or as close as can with keeping maximum image viewable
// e == div[this.slide]
// gets the top and left of the div
var divOffset = getOffset(div);
var imgStyles = getComputedStyle(img);
var divStyles = getComputedStyle(div);
var imgOffset = {
x: parseInt(imgStyles.left),
y: parseInt(imgStyles.top)
};
// where clicked relative in div
var yTravel = e.pageY - divOffset.y;
var xTravel = e.pageX - divOffset.x;
// where clicked
var xOldImg = -imgOffset.x + xTravel;
var yOldImg = -imgOffset.y + yTravel;
// the clicked position relative to the image 0,0
// clicked position will remain at the cursor position while image zoom changes
// calc the same position at the new zoom level
var ratio = zoom / oldZoom;
var xNewImg = xOldImg * ratio;
var yNewImg = yOldImg * ratio;
// calc new top / left
var xStart = -(xNewImg - xTravel);
var yStart = -(yNewImg - yTravel);
img.style.height = parseInt(divStyles.height) * (zoom) + "px";
img.style.width = parseInt(divStyles.width) * (zoom) + "px";
img.style.top = yStart + "px";
img.style.left = xStart + "px";
img.style.cursor = "grab";
}
function resetZoom(div, img) {
img.style.top = "0px";
img.style.left = "0px";
img.style.height = div.style.height;
img.style.width = div.style.width;
img.style.cursor = "default";
zoom = 1;
}
function getOffset(element) {
var rect = element.getBoundingClientRect();
var posX = rect.left + window.pageXOffset; // alias for window.scrollX;
var posY = rect.top + window.pageYOffset; // alias for window.scrollY;
return {
x: posX,
y: posY,
left: posX,
top: posY,
width: rect.width,
height: rect.height
};
}
function round(number, precision) {
precision = precision ? +precision : 0;
var sNumber = number + '',
periodIndex = sNumber.indexOf('.'),
factor = Math.pow(10, precision);
if (periodIndex === -1 || precision < 0) {
return Math.round(number * factor) / factor;
}
number = +number;
// sNumber[periodIndex + precision + 1] is the last digit
if (sNumber[periodIndex + precision + 1] >= 5) {
// Correcting float error
// factor * 10 to use one decimal place beyond the precision
number += (number < 0 ? -1 : 1) / (factor * 10);
}
return +number.toFixed(precision);
}
#div {
width: 350px;
height: 262px;
border: 1px solid black;
overflow: hidden;
}
#img {
width: 350px;
height: 262px;
position: relative;
}
<div id='div'>
<img id='img' src="https://www.design.mseifert.com/git-slideshow/img-demo/images01.jpg">
</div>
Qu'en est-il de l'utilisation de translate3d
et perspective
, pour gérer les transformations 3D, au lieu d'utiliser scale
? En outre, le découplage du zoom de la traduction le simplifie.
$(document).ready(function() {
var translateX = 0,
translateY = 0,
translateZ = 0,
stepZ = 10,
initial_obj_X = 0,
initial_obj_Y = 0,
initial_mouse_X = 0,
initial_mouse_Y = 0;
function apply_coords() {
$("#slide").css("transform", 'perspective(100px) translate3d(' + translateX + 'px, ' + translateY + 'px, ' + translateZ + 'px)');
}
$("#slideContainer").on("mousewheel DOMMouseScroll", function(e) {
e.preventDefault();
var delta = e.delta || e.originalEvent.wheelDelta;
var zoomOut;
if (delta === undefined) {
delta = e.originalEvent.detail;
zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
zoomOut = !zoomOut;
} else {
zoomOut = delta ? delta < 0 : e.originalEvent.deltaY > 0;
}
if (zoomOut) {
translateZ = translateZ - stepZ;
} else {
translateZ = translateZ + stepZ;
}
apply_coords();
});
var is_dragging = false;
$("#slideContainer")
.mousedown(function(e) {
is_dragging = true;
})
.mousemove(function(e) {
if (is_dragging) {
e.preventDefault();
var currentX = e.type === 'touchend' ? e.changedTouches[0].pageX : e.pageX;
var currentY = e.type === 'touchend' ? e.changedTouches[0].pageY : e.pageY;
translateX = initial_obj_X + (currentX - initial_mouse_X);
translateY = initial_obj_Y + (currentY - initial_mouse_Y);
apply_coords();
} else {
initial_mouse_X = e.type === 'touchend' ? e.changedTouches[0].pageX : e.pageX;
initial_mouse_Y = e.type === 'touchend' ? e.changedTouches[0].pageY : e.pageY;
initial_obj_X = translateX;
initial_obj_Y = translateY;
}
})
.mouseup(function() {
is_dragging = false;
});
});
#slideContainer {
width: 200px;
height: 200px;
overflow: hidden;
position: relative;
}
#slide {
width: 100%;
height: 100%;
background: red;
}
img {
width: auto;
height: auto;
max-width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="slideContainer">
<div id="slide">
</div>
</div>