欢迎光临,效果先行:
上为视频,不动轻戳。
如果您是使用PC机器阅读的本文,也可以点击此页面实际操作感受下。
如果查看源码,会发现只有个 img 元素,并无任何其他辅助:
如何实现的呢?下面是正文↓
一、背景之工作需求
最近在做富文本编辑器相关的需求,其中就有上传图片后需要对图片进行拉伸的需求。
比方说下图所示的交互就是腾讯文档中的图片缩放效果:
我以前也多次实现过(图片上传,头像上传,以及一些工具类产品),不过实现方式都是 <img>
元素外面包裹个 DIV,然后定位一些方框框,然后再去拉伸。
如果是非编辑器产品,这么实现并没有多大的问题。
但是如果是需要实时编辑的产品,IMG外面还有其他标签,势必会影响很多编辑操作。当然,还有方法就是JS定位,拖拽层覆盖在图像上,从技术成本上讲,也是一个不错的实现,但如果页面发生了滚动,或者拖拽很快,拖拽的小方块就有可能跟不上(具体要看你的实现)。
所以我就在想,以如今的Web能力,有没有可能无需任何其他借助,单单就一个 <img>
标签,就实现图像的拉伸效果呢?
我脑中迅速遍历了下我所知的技术点,貌似理论上可行。
然后......
二、并没有预想的顺利
原本的设计稿是这样的:
四个角四个圆圈圈,比较简洁,凡是这种在元素边框(不包括边角)包含规则图形(没有图形也是一种规律)的效果,一定是使用CSS border-image
属性。
CSS border-image
属性可以无限外扩,不占据布局空间,非常强,具体语法参见《CSS新世界》对应章节,高级应用参见"被低估的border-image属性"一文。
然而,border-image
属性想要使用顺畅,需要设计师的素材配合,需要按照九宫格布局进行设计,下面就是我在 PS 中给设计师绘制的结构示意:
随后设计师按照此示意结构将图形矢量化(变成 SVG 文件),我再去实现,结果发现了一个棘手的问题,什么问题呢?
直接看图说话吧,下图就是我使用处理后的素材配合border-image
属性实现的效果:
大家看出来问题没有?
可能图有些小,看不到细节,我把边角放大 N 倍看下:
瞧见没有,border-image生成的图形藏在了图像内容的后面。
在Web中,content内容的层级是最高的,outline轮廓、border边框、background背景色等都是比图文内容的层级低的。
因此,border-image的图形在 IMG 元素内容的后面,导致边角的拖拽圈圈显示不全。
有一种解决方法是把 <img>
元素的src
地址改为使用 background-image
背景图实现,但这么做会影响浏览器默认的"复制图像地址","复制图像"等操作,最终的效果只是有形无实,这样的实现效果一定是没法通过马斯克的代码审查,是会被fire解雇的。
当技术无能为力的时候,可以从源头寻找解决方案,那就是......当当当当,改设计,当然,要和设计师提前沟通好。
至于能否沟通成功,一来看设计师的职业素养,二类看你个人的影响力,三是你自己的沟通能力,能不能把利弊方案等提前讲清楚。
所以,我就给设计师提了个方案(自己做了个图示意了下),能否类似这样的拉伸效果:
无疑议,直接通过。
变化在于,将拖拽图形全部改造为在图像元素的外部,这样就不会有被内容覆盖的问题了。
相关 CSS代码如下:
img.resizable, img[resizable] {
border: 3px solid transparent;
border-image: url(./作者zhangxinxu.svg) 12 / 12px / 0;
}
OK,至此,最大的技术难度就解决了。
对,这个案例中,最大的技术难度就是 CSS 这一块,JS其实还好,就是手形和拖拽,这个有几年工作经验的前端都可以轻松驾驭。
三、眼见为实、demo与开源
代码码完上线之后,还有很多其他重要的事情要做。
包括撰写详细的文档,包括把项目中比较创新的地方总结梳理出来,包括项目小结文档和邮件等,这些工作很多人并不喜欢做,但对于个人的职业发展却很重要。
因为这些东西,你不讲,你不说,别人是不知道的,毕竟像马斯克这样会review代码的领导可是很罕见的,你不展现自己,说不定就会被浑水摸鱼的半吊子们取而代之,那多可惜啊。
回到这里。
为了总结自己的工作,同时方便自己日后复用,以及希望可以帮到其他遇到类似需求的人,所以我把相关的功能从生产环境剥离,独立成一个小小的开源项目,放在了 gitee 上了。
项目地址
单IMG元素的图像拉伸效果:https://gitee.com/zhangxinxu/only-img-resize
体验地址:https://zhangxinxu.gitee.io/only-img-resize/
使用说明
一开始我是做出直接引入JS就可以使用的那种,后来想想,有些参数还是必须的,所以还是改成了 export/import 模式。
<script type="module">
import onlyImgResize from './src/onlyImgResize.js';
`onlyImgResize({
// 参数在这里
});
`
</script>
此时,页面中所有设置了类名 resizable,或者设置了 HTML 属性 resizable 的元素都可以四象拉伸。
使用很Easy~
语法和参数
语法如下:
onlyImgResize(options);
options
为可选参数,包括:
selector
:
字符串值。默认值是 '.resizable, [resizable]'
,表示识别为可拉伸图片的选择器。
maxWidth
:
数值或布尔值。默认是 true
,表示有最大宽度限制,最大宽度值是第一个非内联祖先元素的宽度。支持设置为数值,指定最大宽度值。如果设置为false,则表示图片尺寸的拉伸没有最大范围限制。需要注意的是,由于图片设置了透明边框,因为,100%宽度实际上会超出,实际最大宽度应该是 calc(100% - 4px)
。
whenDisabled
: 函数值,如果返回 true,表示禁用图像的拉伸,如果是 false,则拉伸执行。默认值是:
```
function () {
return window.imgResizable === false || document.imgResizable === false;
}
```
表示,如果 `window.imgResizable` 或者 `document.imgResizable` 的值是 `false`,则禁用拉伸。
此参数用在希望某些情况下,图片不被拉伸的时候。
onFinish : 函数值,默认是空函数,拖拽结束的时候触发。如果大家希望在其他事件,例如拖拽之中也有回调方法,fork该项目,然后自己添加即可。
其他
欢迎 Star,也欢迎关注我的 gitee 账户,会不定期更新一些自己的小玩具。
四、大浪淘沙始见金
好了,至此,正文内容就结束了。
其实深究起来,本文内容略"水",还是多以自我展示为目的,真要说多少人会看到此文,多少人会学到这个小技巧,或者说受到启发,我并不抱多大的希望。
精力有限,我的公众号又不同步,掘金粉丝也不少,也懒得转一份(我做一件事情,一定是长期的,临时磨一枪,不如不做),更是让受众下降了不少。
可转念一想,写了快800篇原创技术文章了,哪一篇没有自我展示的目的呢?虽然当下,连我自己都没意识到这一点,但回过头看,实际上已经展示了自己。
也正因为有了这份展示,才能收获影响力,才能继续有动力坚持,纯粹的无人知晓的无私奉献注定是无法持久的,就好比在企业中默默干活却不去表达自己的老黄牛,老好人们。
这类人平时自我感觉不错,我对得起我自己,但遇到晋升绩效这样的事情的时候,因为结果不如意,又会感叹不公。
我称这类人为沉浸在让自己舒适的精神世界中的懒惰者,看起来干活勤勉,实际上是个懒人。
即使苦口婆心地反复强调,一定要花点功夫让同事和leader知道你做的工作,无论是量多,活苦,或者亮点都可以,但就是不做,宁可多接几个需求,就是不愿意做这样的事情,为什么呢?
因为做这种事情,跟他的个人作风,跟他的精神世界不符合,如果没有强迫的压力(如KPI之类的),绝对不会去打破这种精神舒适区的,会觉得不安全,直接躺平。
哎呀,这样冒头会不会不好啊?同事会怎样看我啊?项目没什么亮点,还发出去,会不会掉逼格啊?领导怎么看我啊?算了算了,同事都不发,我也懒得弄,做人要低调。
小说中有个桥段经典不衰,就是平时默默无闻,关键时候一鸣惊人,惊诧众人。
为何这样的桥段多呢?因为大多数的人平时就是默默无闻的,同时希望自己关键时候技惊四方。
喂喂喂,诸位,小说看多了吧,真以为是金字总会发光啊,那要需要有大浪淘沙的环境啊!
我们所处的时代,我们所在的团队,大多都是稳定状态的呀,是死水,是缓流,金字由于比重大,沉得更快啊,亲们!
平时不努力,关键时候想要让别人对你知根知底,是很危险的。
算了,不多扯了,家里领导喊我吃晚饭了。
总结下吧,平时多装逼,装着装着,你说不定真就牛逼了,平时少扮猪,装着装着,别人就真认为你是猪了。
对了,记得将此文分享到朋友圈,让你的leader知道你平时是爱学习的。
(本篇完)