Увеличение точки колеса мыши (с помощью масштабирования и перевода)
этот вопрос похож на этот: увеличить точку (используя масштаб и перевести) или даже это:увеличение изображения по центру позиции мыши но я не хочу делать это на холсте, а на обычном изображении (или, скорее, контейнере div изображения). Поэтому масштабирование должно быть как Google maps. Я фактически взламываю / улучшаю iDangerous Swiper zoom (http://idangero.us/swiper/), и это моя отправная точка, и это то, что я получил так далеко: https://jsfiddle.net/xta2ccdt/3/
масштабирование только колесиком мыши. При первом приближении он увеличивается идеально, но я не могу понять, как вычислить каждый зум после первого.
вот мой код: 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%;
}
Я также понял, что если вычесть предыдущие значения translateX и translateY из текущих, я могу увеличить ту же точку столько, сколько хочу и он будет отлично масштабироваться, но если я увеличу одну точку, а затем изменю положение мыши и увеличу снова, он больше не будет масштабироваться, как это должно быть. Пример: https://jsfiddle.net/xta2ccdt/4/
Если я изменю положение мыши, и вычислить разницу X и Y между старой и новой позицией мыши и добавить, что в расчет diff он будет масштабировать правильно во второй раз. Но в третий раз похоже, что эта разница все еще вычитается из общей суммы расчет и это приведет к переводу, чтобы переместить изображение снова, после этого, если мы удерживаем мышь в том же положении он будет масштабироваться правильно снова. Поэтому я решил, что я просто добавлю разницу между старой и новой позицией мыши каждый раз, когда я вычисляю новый "diff", и этот вид работает, больше нет прыжка, как это было, когда я перестал добавлять разницу в позиции мыши, но он все еще не масштабируется на той же позиции, с каждым новым зумом он перемещает (смещает) изображение на небольшое расстояние. сумма. Я полагаю, это потому, что каждый раз появляется новое значение масштабирования, но смещение не линейно, оно каждый раз меньше, приближаясь к нулю, и я не могу понять, как компенсировать смещение. Вот новый пример: https://jsfiddle.net/xta2ccdt/5/ Новое изображение в Примере: старое больше не доступно:https://jsfiddle.net/xta2ccdt/14/
4 ответов
Вы были близки к нему, однако лучше хранить x,y и масштабировать отдельно и вычислять преобразования на основе этих значений. Это делает вещи намного проще + экономит ресурсы (нет необходимости искать свойства dom снова и снова),
Я поместил код в хороший модуль:
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+')')
}
}
используйте его, позвонив
new ScrollZoom($('#container'),4,0.5)
параметры:
- container: оболочка элемента, который будет увеличен. Сценарий ищите первый дочерний элемент контейнера и применить преобразования к нему.
- max_scale: максимальный масштаб (4 = 400% зум)
- фактор: скорость масштабирования (1 = +100% зум на ТИК колесика мыши)
Я думаю, что это приблизит вас к тому, чего вы пытаетесь достичь.
Ключевые Изменения
- я вытащил шкалу за пределы обратного вызова; я не думаю, что вы хотите сбросить шкалу на каждом событии колеса.
- вместо того, чтобы вычислять перевод вручную, попробуйте установить
transform-origin
быть центрированным на мыши (Если вы не хотите, чтобы держать его в центре, который является по умолчанию)
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>
как насчет использования translate3d
и perspective
, для обработки 3D-преобразований вместо использования scale
? Кроме того, развязка масштабирования от перевода упрощает его.
$(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>
код, который я использую для увеличения в положении мыши ниже. Он не использует transform
/ translate3d
но корректирует положение изображения в div и регулирует его height
и 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>