繁体   English   中英

使用CSS transform scale()放大元素而不进行裁剪,保持滚动

[英]Using CSS transform scale() to zoom into an element without cropping, maintaining scrolling

实例: https//jsfiddle.net/b8vLg0ny/

可以使用CSS scaletranslate函数来放大元素。

以这个例子为例,在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;
  }
});

通过控制translateXtranslateY的值,您可以更改缩放的工作方式。

初始渲染视图看起来像这样:

初始渲染视图

单击A框将适当放大您的位置:

缩放到A然后缩小

(注意,在末尾单击D只是通过缩小显示重置。)

问题是 :缩放到框D将缩放缩放容器,使得滚动到顶部和左边不起作用,因为内容溢出。 当缩放到方框B(左半部分被裁剪)和C(上半部分被裁剪)时也会发生同样的情况。 仅使用A,内容不会溢出容器外部。

在与缩放相关的类似情况下(参见CSS3变换比例和带溢出的容器 ),一种可能的解决方案是指定transform-origin: top left (或0 0 )。 由于缩放相对于左上角的工作方式,滚动功能保持不变。 但这似乎不适用于此,因为这意味着您不再重新定位要关注点击框(A,B,C或D)的内容。

另一种可能的解决方案是在缩放容器中添加margin-leftmargin-top ,这样可以增加足够的空间来弥补溢出的内容。 但同样:翻译价值不再排队。

那么:有没有办法既可以放大给定的元素, 可以通过滚动来溢出,这样内容就不会被裁剪?

更新 :通过动画scrollTopscrollLeft有一个粗略的近乎解决方案,类似于https://stackoverflow.com/a/31406704/528044 (参见jsfiddle示例 ),但它不是一个合适的解决方案,因为它首先缩放到顶部离开,而不是预定的目标。 我开始怀疑这实际上是不可能的,因为它可能相当于要求scrollLeft为负数。

为什么不将TransformOrigin重新定位到0 0并在动画使用正确的scrollTop/scrollLeft

如果您不需要动画,则TransformOrigin始终保持为0 0并且仅使用滚动来显示该框。

为了使动画不那么跳跃,只使用transform属性的transform-origin ,否则transform-origin也会被动画化。 我已经用4x4元素编辑了这个例子,但我认为将一个盒子完全缩放到视图中是有意义的,这就是我改变缩放级别的原因。 但是,如果你保持缩放级别2和网格大小15x15 ,那么使用这种方法真正精确的原点应该计算变换,然后也正确的滚动。

无论如何我不知道,如果你发现这种方法有用。

堆栈代码段

 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 }; } 
 * { 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 } 
 <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> 

更新

我卡在滚动条上并没有一直显示,所以我需要调查那个部分,以便代码被注释掉,而我使用延迟将点击的框移动到视图中。

这是用来玩的小提琴演示 ,以弄清楚如何解决滚动条问题。

旁注:在@AVAVT发表的评论中,我想链接到他的帖子 ,因为这可能对其他人有所帮助,在某些情况下我认为这是一个有趣的替代方案。

 (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); } }); })(); 
 * { 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; } 
 <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> 

我正在回答我自己的问题,因为我相信它实际上不可能满足给定的要求。 至少并非没有一些会在视觉上引起问题的hackery,例如,在将transform-origin切换为0, 0之后通过动画scrollTop进行跳跃滚动(通过将所有内容放回容器中来移除裁剪)。

我喜欢有人证明我错了,但这似乎相当于要求scrollLeft = -10MDN告诉你的东西是不可能的 (“如果设置为小于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;
}

我从另一个问题拼凑了这个( 在纯js中移动(拖动/平移)和缩放对象(图像或div) ),其中widthheight被改变。 这在我的情况下并不完全适用,因为我需要放大页面上的特定元素(有很多框而不是2x2网格)。 该问题的解决方案( https://jsfiddle.net/j8kLz6wm/1/ )显示了纯JavaScript中的基本方法。 如果你有jQuery可用,你可以使用jquery.panzoom

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM