不知各位遇到特别长的图片时是怎么处理的?
是 截取符合长宽的部分做临时展示?
还是 硬要长宽100%模糊(啥也看不清)展示?
还是 先拿一个压缩的图片做占位,在鼠标移入或点击时放大预览?
今天偶然打开PC端QQ空间时,我发现了一种似乎更好的方式 —— 鼠标移入时在范围内上下滚动图片预览,移出时停止滚动。直到用户点击图片跳转到详情展示:
分析
这种方式着实让我“眼前一亮”,一定程度上带给了用户新奇的体验感。顺着思路,一键 f12 打开源码,我看到了这样的代码:
显而易见,QQ应该是采用了js监听鼠标位置的做法,动态改变 img
标签中自定义属性的值,并根据此去改变图片的 margin-top
值,用 transition
属性去制造动画效果。
模拟实现
为了方便些,这里笔者采用两个空标签分割“包裹图片的元素”,然后分别在上面监听鼠标事件的做法,实现效果如下:
首先,“科普”几个API,它们将会是你的助力:
-
image.naturalHeight
:看到前面的image没?这是用来获取图片原始高度的(同系的还有image.naturalWidth
,你可以用它来确定包裹元素的最大/最小宽度); -
dom.offsetTop
:offset系的API,用来获取dom元素和离它最近的父元素顶部的距离(同系的还有offsetLeft
、offsetWidth/offsetHeight
(返回元素的像素宽高,包含该元素的内边距和边框,是一个整数且不包含:before或:after等伪类元素的宽高)、offsetParent
(获取父元素)); -
dom.getBoundingClientRect()
:它有四个常用值:left、top、right、bottom,分别是相对于当前视口(即此tab网页窗口左侧、顶部、右侧、底部)的位置; -
dom.scrollHeight
:scroll系的API,用来获取元素的真实高度(同系的还有scrollWidth/scrollLeft/scrollTop
),一般不会用它来作用于图片上,因为它必须等元素加载出来才能确定; -
window.innerHeight
:inner系的API,它们只作用在window对象上,返回窗口的文档显示区的高度(同系的还有一个window.innerWidth
) <-> 相对的两个outerWidth
和outerHeight
,用于获取加上工具条与滚动条窗口的宽度与高度;
顺便说一句,像
img.getBoundingClientRect().top
、img.offsetTop
这些都是 只读 值,所以不要妄想用它们来改变元素位置!
布局如下:
这里class为before和after的两个标签就是前面所说的“占位”元素(至于QQ是怎么实现的,等笔者稍作研究后再回来更新),它们负责判断“图片是应该向上滑还是向下滑”!
本来这里笔者想采用伪元素的方式:用
::before
和 ::after
占位并触发事件,但是在查遍资料以后我突然想到一件事:不是经常说伪元素的优势是脱离文档流吗?那还如何能够获取到?
唉,大意了,,,
对img元素设置一个初始的margin-top,就是为了配合下面的transition使得在js中改变top值时能够有动画效果!
有了上面的布局方式和API解读,其实js实现就非常简单了 —— 根据上面分析的按部就班来就行:
至此,效果就全部实现了。
但是如果你仔细看,你会发现由于transition动画效果的时间是固定的,在向上/下滑动过短的情况下再向下/上滑动那么滑动的会特别慢!
!当然,我们可以改变策略,让图片的 margin-top
不断--
或++
直到临界值,但这样势必会带来巨大的性能开销。
再回到PC端QQ空间 —— 我们发现,它的transition时间竟然是动态变化的:
这…我猜测可能是设定了一个从上到下固定的时间,然后在JS中按照滑出部分高度(已经滑动的距离)占总高度的比例动态调节时间。。。相关代码笔者正在尝试ing
当然,本文对QQ前端团队对图片的处理来说也许只是沧海一粟,,,更多的还有比如:根据图片整体平均色差调整说明文字的颜色黑/白(canvas-getImageData
API)、图片内容的延迟展示、多图上传性能调优(promise API)等等。
那咱就 再见了?