实例:https://jsfiddle.net/b8vLg0ny/
可以使用CSS scale
和translate
函数来放大元素。
以2x2网格中的4个方框为例。
HTML:
<div id="container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
</div>
</div>
CSS:
* { margin: 0; }
body, html { height: 100%; }
#container {
height: 100%;
width: 50%;
margin: 0 auto;
}
#zoom-container {
height: 100%;
width: 100%;
transition: all 0.2s ease-in-out;
}
.box {
float: left;
width: 50%;
height: 50%;
color: white;
text-align: center;
display: block;
}
.red { background: red; }
.blue { background: blue; }
.green { background: green; }
.black { background: black; }
JavaScript的:
window.zoomedIn = false;
$(".box").click(function(event) {
var el = this;
var zoomContainer = $("#zoom-container");
if (window.zoomedIn) {
console.log("resetting zoom");
zoomContainer.css("transform", "");
$("#container").css("overflow", "auto");
window.zoomedIn = false;
} else {
console.log("applying zoom");
var top = el.offsetTop;
var left = el.offsetLeft - 0.25*zoomContainer[0].clientWidth;
var translateY = 0.5*zoomContainer[0].clientHeight - top;
var translateX = 0.5*zoomContainer[0].clientWidth - left;
$("#container").css("overflow", "scroll");
zoomContainer.css("transform", "translate(" + 2 * translateX + "px, " + 2 * translateY + "px) scale(2)");
window.zoomedIn = true;
}
});
通过控制translateX
和translateY
的值,您可以更改缩放的工作方式。
初始渲染视图如下所示:
单击A框将适当放大您的位置:
(请注意,在末尾单击D只是通过缩小显示重置。)
问题是:缩放到框D将缩放缩放容器,使得滚动到顶部和左边不起作用,因为内容溢出。当缩放到方框B(左半部分被裁剪)和C(上半部分被裁剪)时也会发生同样的情况。仅使用A,内容不会溢出容器外。
在与缩放相关的类似情况下(请参阅CSS3 Transform Scale and Container with Overflow),一种可能的解决方案是指定transform-origin: top left
(或0 0
)。由于缩放相对于左上角的工作方式,滚动功能保持不变。但这似乎并不适用于此,因为这意味着您不再重新定位要关注点击框(A,B,C或D)的内容。
另一种可能的解决方案是向缩放容器添加margin-left
和margin-top
,这会增加足够的空间来弥补溢出的内容。但同样:翻译价值不再排列。
那么:有没有办法两个放大一个给定的元素,和溢出滚动,这样内容就不会被裁剪?
更新:通过设置scrollTop
和scrollLeft
动画来实现粗略的解决方案,类似于https://stackoverflow.com/a/31406704/528044(请参阅the jsfiddle example) ,但它不是一个合适的解决方案,因为它首先放大到左上角,而不是预定的目标。我开始怀疑这实际上是不可能的,因为它可能等同于要求scrollLeft
为负面。
答案 0 :(得分:4)
为什么不将TransformOrigin
重新定位到0 0
并在动画后使用正确的scrollTop/scrollLeft
?
如果您不需要动画,TransformOrigin
可以始终保持0 0
,并且仅使用滚动来显示该框。
为了使动画不那么跳跃,只使用transform
属性的过渡,否则transform-origin
也会被动画化。我已使用4x4
元素编辑了示例,但我认为将一个框完全缩放到视图中是有意义的,这就是我更改缩放级别的原因。但是,如果你继续使用缩放级别2和网格大小15x15
,那么使用这种方法应该为变换计算真正精确的原点,然后还要正确滚动。
无论如何,如果你发现这种方法很有用,我也不知道。
Stack snippet
var zoomedIn = false;
var zoomContainer = $("#zoom-container");
$(".box").click(function(event) {
var el = this;
if (zoomedIn) {
zoomContainer.css({
transform: "scale(1)",
transformOrigin: "0 0"
});
zoomContainer.parent().scrollTop(0).scrollLeft(0);
zoomedIn = false;
return;
}
zoomedIn = true;
var $el = $(el);
animate($el);
zoomContainer.on('transitionend', function(){
zoomContainer.off('transitionend');
reposition($el);
})
});
var COLS = 4, ROWS = 4,
COLS_STEP = 100 / (COLS - 1), ROWS_STEP = 100 / (ROWS - 1),
ZOOM = 4;
function animate($box) {
var cell = getCell($box);
var col = cell.col * COLS_STEP + '%',
row = cell.row * ROWS_STEP + '%';
zoomContainer.parent().css('overflow', 'hidden');
zoomContainer.css({
transition: 'transform 0.2s ease-in-out',
transform: "scale(" + ZOOM + ")",
transformOrigin: col + " " + row
});
}
function reposition($box) {
zoomContainer.css({
transition: 'none',
transform: "scale(" + ZOOM + ")",
transformOrigin: '0 0'
});
zoomContainer.parent().css('overflow', 'auto');
$box.get(0).scrollIntoView();
}
function getCell ($box) {
var idx = $box.index();
var col = idx % COLS,
row = (idx / ROWS) | 0;
return { col: col, row: row };
}
&#13;
* { margin: 0; }
body, html { height: 100%; }
#container {
height: 100%;
width: 50%;
margin: 0 auto;
overflow: hidden;
}
#zoom-container {
height: 100%;
width: 100%;
will-change: transform;
}
.box {
float: left;
width: 25%;
height: 25%;
color: white;
text-align: center;
}
.red { background: red; }
.blue { background: blue; }
.green { background: green; }
.black { background: black; }
.l { opacity: .3 }
&#13;
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<div id="container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
<div class="box red l">E</div>
<div class="box blue l">F</div>
<div class="box green l">G</div>
<div class="box black l">H</div>
<div class="box red">I</div>
<div class="box blue">J</div>
<div class="box green">K</div>
<div class="box black">L</div>
<div class="box red l">M</div>
<div class="box blue l">N</div>
<div class="box green l">O</div>
<div class="box black l">P</div>
</div>
</div>
&#13;
答案 1 :(得分:1)
更新
我卡在滚动条上并没有一直显示,所以我需要调查那部分,以便代码被注释掉,而我使用延迟将点击的框移动到视图中。
这是我用来玩的my fiddle demo,以找出如何解决滚动条问题。
旁注:在@AVAVT发表的评论中,我想链接到his post here,因为这可能对其他人有所帮助,在某些情况下我觉得这是一个有趣的选择。
(function(zoomed) {
$(".box").click(function(event) {
var el = this, elp = el.parentElement;
if (zoomed) {
zoomed = false;
$("#zoom-container").css({'transform': ''});
} else {
zoomed = true;
/* this zooms correct but show 1 or none scroll for B,C,D so need to figure out why
var tro = (Math.abs(elp.offsetTop - el.offsetTop) > 0) ? 'bottom' : 'top';
tro += (Math.abs(elp.offsetLeft - el.offsetLeft) > 0) ? ' right' : ' left';
$("#zoom-container").css({'transform-origin': tro, 'transform': 'scale(2)'});
*/
$("#zoom-container").css({'transform-origin': '0 0', 'transform': 'scale(2)'});
/* delay needed before scroll into view */
setTimeout(function() {
el.scrollIntoView();
},250);
}
});
})();
&#13;
* { margin: 0; }
body, html { height: 100%; }
#container {
height: 100%;
width: 50%;
overflow: auto;
margin: 0 auto;
}
#zoom-container {
height: 100%;
width: 100%;
transition: all 0.2s ease-in-out;
}
.box {
float: left;
width: 50%;
height: 50%;
color: white;
text-align: center;
display: block;
}
.red {
background: red;
}
.blue {
background: blue;
}
.green {
background: green;
}
.black {
background: black;
}
&#13;
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<div id="container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
</div>
</div>
&#13;
答案 2 :(得分:1)
我正在回答我自己的问题,因为我相信它实际上不可能满足给定的要求。至少并非没有一些会在视觉上引起问题的hackery,例如,在将scrollTop
切换为transform-origin
之后通过动画0, 0
进行跳跃式滚动(通过将所有内容放回容器中来移除裁剪)。
我喜欢某人证明我错了,但这似乎等同于scrollLeft = -10
,MDN will tell you is not possible。 (“如果设置为小于0 [...]的值,则scrollLeft设置为0.”)
但是,如果将UI从滚动,缩放和拖动/平移更改为可接受,则可以实现:https://jsfiddle.net/jegn4x0f/5/
这是与原始问题具有相同背景的解决方案:
HTML:
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<button id="zoom-out">Zoom out</button>
<div id="container">
<div id="inner-container">
<div id="zoom-container">
<div class="box red">A</div>
<div class="box blue">B</div>
<div class="box green">C</div>
<div class="box black">D</div>
</div>
</div>
</div>
JavaScript的:
//
// credit for the approach goes to
//
// https://stackoverflow.com/questions/35252249/move-drag-pan-and-zoom-object-image-or-div-in-pure-js#comment58224460_35253567
//
// and the corresponding example:
//
// https://jsfiddle.net/j8kLz6wm/1/
//
// in a real-world setting, you
// wouldn't keep this information
// on window. this is just for
// the demonstration.
window.zoomedIn = false;
// stores the initial translate values after clicking on a box
window.translateY = null;
window.translateX = null;
// stores the incremental translate values based on
// applying the initial translate values + delta
window.lastTranslateY = null;
window.lastTranslateX = null;
// cursor position relative to the container, at
// the time the drag started
window.dragStartX = null;
window.dragStartY = null;
var handleDragStart = function(element, xCursor, yCursor) {
window.dragStartX = xCursor - element.offsetLeft;
window.dragStartY = yCursor - element.offsetTop;
// disable transition animations, since we're starting a drag
$("#zoom-container").css("transition", "none");
};
var handleDragEnd = function() {
window.dragStartX = null;
window.dragStartY = null;
// remove the individual element's styling for transitions
// which brings back the stylesheet's default of animating.
$("#zoom-container").css("transition", "");
// keep track of the translate values we arrived at
window.translateY = window.lastTranslateY;
window.translateX = window.lastTranslateX;
};
var handleDragMove = function(xCursor, yCursor) {
var deltaX = xCursor - window.dragStartX;
var deltaY = yCursor - window.dragStartY;
var translateY = window.translateY + (deltaY / 2);
// the subtracted value here is to keep the letter in the center
var translateX = window.translateX + (deltaX / 2) - (0.25 * $("#inner-container")[0].clientWidth);
// fudge factor, probably because of percentage
// width/height problems. couldn't really trace down
// the underlying cause. hopefully the general approach
// is clear, though.
translateY -= 9;
translateX -= 4;
var innerContainer = $("#inner-container")[0];
// cap all values to prevent infinity scrolling off the page
if (translateY > 0.5 * innerContainer.clientHeight) {
translateY = 0.5 * innerContainer.clientHeight;
}
if (translateX > 0.5 * innerContainer.clientWidth) {
translateX = 0.5 * innerContainer.clientWidth;
}
if (translateY < -0.5 * innerContainer.clientHeight) {
translateY = -0.5 * innerContainer.clientHeight;
}
if (translateX < -0.5 * innerContainer.clientWidth) {
translateX = -0.5 * innerContainer.clientWidth;
}
// update the zoom container's translate values
// based on the original + delta, capped to the
// container's width and height.
$("#zoom-container").css("transform", "translate(" + (2*translateX) + "px, " + (2*translateY) + "px) scale(2)");
// keep track of the updated values for the next
// touchmove event.
window.lastTranslateX = translateX;
window.lastTranslateY = translateY;
};
// Drag start -- touch version
$("#container").on("touchstart", function(event) {
if (!window.zoomedIn) {
return true;
}
var xCursor = event.originalEvent.changedTouches[0].clientX;
var yCursor = event.originalEvent.changedTouches[0].clientY;
handleDragStart(this, xCursor, yCursor);
});
// Drag start -- mouse version
$("#container").on("mousedown", function(event) {
if (!window.zoomedIn) {
return true;
}
var xCursor = event.clientX;
var yCursor = event.clientY;
handleDragStart(this, xCursor, yCursor);
});
// Drag end -- touch version
$("#inner-container").on("touchend", function(event) {
if (!window.zoomedIn) {
return true;
}
handleDragEnd();
});
// Drag end -- mouse version
$("#inner-container").on("mouseup", function(event) {
if (!window.zoomedIn) {
return true;
}
handleDragEnd();
});
// Drag move -- touch version
$("#inner-container").on("touchmove", function(event) {
// prevent pull-to-refresh. could be smarter by checking
// if the page's scroll y-offset is 0, and even smarter
// by checking if we're pulling down, not up.
event.preventDefault();
if (!window.zoomedIn) {
return true;
}
var xCursor = event.originalEvent.changedTouches[0].clientX;
var yCursor = event.originalEvent.changedTouches[0].clientY;
handleDragMove(xCursor, yCursor);
});
// Drag move -- click version
$("#inner-container").on("mousemove", function(event) {
// prevent pull-to-refresh. could be smarter by checking
// if the page's scroll y-offset is 0, and even smarter
// by checking if we're pulling down, not up.
event.preventDefault();
// if we aren't dragging from anywhere, don't move
if (!window.zoomedIn || !window.dragStartX) {
return true;
}
var xCursor = event.clientX;
var yCursor = event.clientY;
handleDragMove(xCursor, yCursor);
});
var zoomInTo = function(element) {
console.log("applying zoom");
var top = element.offsetTop;
// the subtracted value here is to keep the letter in the center
var left = element.offsetLeft - (0.25 * $("#inner-container")[0].clientWidth);
var translateY = 0.5 * $("#zoom-container")[0].clientHeight - top;
var translateX = 0.5 * $("#zoom-container")[0].clientWidth - left;
$("#container").css("overflow", "scroll");
$("#zoom-container").css("transform", "translate(" + (2*translateX) + "px, " + (2*translateY) + "px) scale(2)");
window.translateY = translateY;
window.translateX = translateX;
window.zoomedIn = true;
}
var zoomOut = function() {
console.log("resetting zoom");
window.zoomedIn = false;
$("#zoom-container").css("transform", "");
$("#zoom-container").css("transition", "");
window.dragStartX = null;
window.dragStartY = null;
window.dragMoveJustHappened = null;
window.translateY = window.lastTranslateY;
window.translateX = window.lastTranslateX;
window.lastTranslateX = null;
window.lastTranslateY = null;
}
$(".box").click(function(event) {
var element = this;
var zoomContainer = $("#zoom-container");
if (!window.zoomedIn) {
zoomInTo(element);
}
});
$("#zoom-out").click(function(event) {
zoomOut();
});
CSS:
* {
margin: 0;
}
body,
html {
height: 100%;
}
#container {
height: 100%;
width: 50%;
margin: 0 auto;
}
#inner-container {
width: 100%;
height: 100%;
}
#zoom-container {
height: 100%;
width: 100%;
transition: transform 0.2s ease-in-out;
}
.box {
float: left;
width: 50%;
height: 50%;
color: white;
text-align: center;
display: block;
}
.red {
background: red;
}
.blue {
background: blue;
}
.green {
background: green;
}
.black {
background: black;
}
我将其与另一个问题(Move (drag/pan) and zoom object (image or div) in pure js)拼凑在一起,其中width
和height
正在被更改。这在我的情况下并不完全适用,因为我需要放大页面上的特定元素(有很多框而不是2x2网格)。该问题的解决方案(https://jsfiddle.net/j8kLz6wm/1/)显示了纯JavaScript中的基本方法。如果你有jQuery,你可以使用jquery.panzoom
。