原生JS手写丝滑流畅的元素拖拽效果

提到元素拖拽,通常都会先想到用 HTML5 的拖拽放置 (Drag 和 Drop) 来实现,它提供了一套完整的事件机制,看起来似乎是首选的解决方案,但实际却不是那么美好,主要是它的样式太过简陋,无法实现更高级的用户体验:

这是浏览器默认的拖拽效果,点住拖拽任意图片或文字都会产生。

笔者因为之前有个小项目需要经常参考稿定设计,一直有留意其元素拖拽的效果(如下图),所以接下来我将以这种效果为蓝本,使用原生 JS 实现一个富有动感的 自定义拖拽 效果,话不多说直接开摸。

首先说下思路,我们需要知道鼠标的三个事件,分别是 mousedownmousemovemouseup ,当点击按下的时候,克隆一个绝对定位的元素,并标识下\”拖拽中\”的状态,接着在 mousemove 中就可以判断应该执行的具体方法,从而让元素随着鼠标移动起来。

在监听事件的 event 对象中,有几个参数是比较重要的:clientXclientY 标识的鼠标当前横坐标和纵坐标,offsetXoffsetY 表示相对偏移量,可以在 mousedown 鼠标按下时记录初始坐标,在 mouseup 鼠标抬起时判断是否在目标区域中,如果是则用鼠标获取到的当前的偏移量 – 初始坐标得到元素实际在目标区域中的位置。

为了阅读体验,以下所有代码均有部分省略,文末可查看完整源码地址,代码量并不多。

先简单实现一个两栏布局界面,并应用上一些 CSS 效果:

利用滤镜 filter: brightness(90%); 调节明亮度可以快速实现一个鼠标覆盖的动态效果,无需额外制作遮罩:

使用伪类激活 cursorgrabgrabbing 可以设置抓取动作的图标:

利用事件委托机制为选择列表添加 mousedown 事件监听,实现抓取的原理是在鼠标按下时克隆按下的元素,并把克隆出来的元素设置成绝对定位,让它\”浮\”起来:

将鼠标的坐标设置为克隆元素的绝对定位值(lefttop),就会像下图所示这样,此时减去 offset 偏移量,就能让克隆元素覆盖在本体上面。

初始化的值需要记录起来方便后续计算,同时我们用 dragging 变量标记了状态(拖动中),接下来配合移动鼠标的监听事件就能将元素“抓”起来了:

上面只是实现了元素的拖动,但是\”克隆\”的效果实在太明显了,为了让元素看起来更像是拖出来的而不是复制出来的,我们还要让本体隐藏,同时DOM结构不能丢失,这时只需在按下拖动时给本体元素设置个 opacity: 0,结束时再改回透明度1就能搞定。

虽然到这功能就算实现了,但实际效果还是有点僵硬,参考稿定设计中的元素放开时会固定回到一个位置,然后再收回去,这个过渡又有点鬼畜,不够流畅。其实只需让元素回退过程有一个自然地动画就行,transition 就能实现:

最终我在动作结束时给克隆元素添加了过渡属性,然后直接设置回初始坐标让克隆元素回到它的出生地点,用定时器在过渡动画持续的相同时间后移除克隆元素,这样就有了一个平滑稳定的回退动画。

由于在改变元素状态的过程中需要频繁进行多个 CSS 操作,为降低回流重绘的成本,最好将多个操作合并起来处理,这里利用了 cssText 来实现:

放大我们可以使用 transform: scale 来实现,只需要将拖动位置之间的距离当做变化系数(假设为d),那么scale变化数值即为(元素宽度 + d)/元素宽度,而放大的最终倍数必定为 图片实际宽度/元素的宽度,只要判断不超过这个边界就可以。(这个图片实际宽高在真实业务场景中建议在上传资源时就记录在数据库,这里我是模拟的随机一个原图尺寸)。

两点间距离计算公式为:

代码实现:

效果演示:

注意元素都要设置 transform-origin: top left; 改变缩放原点到左上角,否则默认(中心为原点)的转换会发生比较明显的偏移。

其实拖拽放置有点像是\”复制\”与\”粘贴\”,前面我们实现了复制,放置主要就是将元素粘贴到画布当中,流程步骤如下:

  1. 如果鼠标在目标区域,拷贝元素到画布中,如果不在画布中,执行倒退动画
  2. 2. 删除元素

判断是否在画布内抬起很简单,往画布上绑定mouseup监听事件即可,克隆的新元素必须删除无用的属性和class,此时设置元素的lefttop即可将元素放置进画布中,关键点在于画布内的target有可能是错的,因为如果鼠标抬起的区域已经放置了元素,那么相对偏移量就得我们自己计算了,使用getBoundingClientRect方法获取画布本身相对于视窗的偏移,鼠标坐标减去画布本身的偏移就是元素在画布中的位置了。

只贴了部分关键代码,完整代码文末查看。

如果不对边界情况进行处理可能会导致拖动时发生意外的中断,无法正确回收克隆元素。

参考稿定设计中元素拖拽是直接赋值原图的,原图大小通常无法控制,免不了需要加载时间,造成卡顿空白的问题,在网络不够快时体验尤其尴尬:

我的优化思路是利用浏览器加载过同一张图片就会优先读缓存的机制,先用一个Image加载原图,等其加载完毕再把拖拽元素的src改成原图,这样浏览器会\”自动\”帮我们优化这个过程,只需要注意一点,由于这是个异步任务,所以一定要做好对应标记,不然手速快的时候控制不好触发顺序。

效果演示,故意加大了图片的分辨率差异:

以上就是文章的全部内容,感谢看到这里,希望对你有所帮助或启发!创作不易,如果觉得文章写得不错,可以点赞收藏支持一下,也欢迎关注,我会更新更多实用的前端知识与技巧,我是茶无味的一天,期待与你共同成长~

[1] 完整代码地址: https://juejin.cn/post/7145447742515445791/#heading-9[2] 关于作者: https://book.palxp.com

ztext – 简单几行代码创建酷炫3D特效文字的开源JS库

把网页上的文字变成酷炫的 3D 风格,还能制作旋转动效,有了 ztext.js,只需要几行代码。

ztext 能做什么

ztext.js 是一个能把常规的平面文字变成 3D 样式的前端开源代码库,让开发者能通过非常简单的 api 创造出酷炫的 3D 文字效果。配合 CSS 动画,可以实现惊艳酷炫的交互效果。

ztext 官网文档截图

ztext.js 库的特点

  • 轻量无依赖,支持原生 javascript 和在 Vue / React 中安装使用
  • 性能强劲,兼容性强。基于 CSS 的 transform-style 属性实现,超过98%用户的浏览器都支持,而且用于 CSS transform 的动画性能很高
  • 功能强大,适用范围广。不仅适用于任何中西文字体,还支持图片/ svg / Emoji 表情符号

支持图片/svg/表情符号

开发上手体验

在开发 PC 端展示型的页面或应用时,运用合适的特效,能让呈现效果印象深刻。把 banner 上的标题变成 3D,搭配设计精美的元素,是一个很见的需求。而常规的方法是使用 canvas,前端需要加载几M大小的 WebGL 库,折腾数小时才能实现。

ztext.js 就是一个适用于这样场景的 js 库,压缩版本大小仅3.8kb,api 也非常简单,而且官网有详细的代码例子,甚至不需要有太扎实的代码基础,copy 改改就能用,快速创建酷炫的 3D 文字特效。配合鼠标悬停的交互,着实能为产品增色不少。

ztext.js 实现 3D 文字的原理

ztext.js 并没有引入真正的第三方 3D 库来实现三维视图,而是通过从 HTML 元素创建图层,来给人一种立体的错觉,基于 CSS 的 transform-style 属性来还原 3D ,视觉效果非常好,作为用户没有发现任何区别,而且很容易结合 CSS3 动画来使用,能够随时调整角度,非常喜欢这种简单酷炫的方式。

免费开源说明

ztext.js 基于 MIT 开源协议在 Github 上开源,任何人都可免费下载用在自己的项目,包括商业用途。

关注我,持续分享高质量的免费开源、免费商用的资源。

↓↓点【了解更多】查看本次分享的网址。

5行代码带你实现一个js的打字效果

有次看电影, 看到屏幕上一个个的文字蹦出来, 感觉像是有人在打字一样, 觉得挺有意思, 于是这里也用js实现了一个。

效果预览

最简单的打字效果

只要五行代码, 这里直接贴出来:

是不是超级简单, 现在只要执行这个函数, 你就能看到这样的效果了:

实现原理:

通过setTimeout设置一个时间的间隔, 每隔一定时间向dom中插入对应的数据, 并将index加1, 然后重新调用这个写函数。

我自己是一名从事了多年开发的web前端老程序员,目前辞职在做自己的web前端私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的web前端学习干货,各种框架都有整理,送给每一位前端小伙伴,想要获取的可以关注我的头条号并在后台私信我:前端,即可免费获取。

在字的后面加上模拟的闪烁光标

思路:

通过一个伪类after将光标定在dom的最后面, 并给一个显示隐藏的循环动画模拟光标的闪烁

通过css3的animation我们就实现了一个闪烁的光标效果。

实现:

我们可以把上面的代码放在一个class中, 然后我们执行writing时将这个class加到对应的dom中即可。

实现输入汉字加拼音

这里依赖一个插件pinyin, 大家可以通过npm安装

使用也很简单,我们只要把文本传入这个插件的方法中, 返回的就是每个汉字的拼音的数组。

然后我们通过模板字符串注入到对应的文字后面即可:

这样就实现了加拼音的简单效果了。

加动画

加动画的代码也很简单, 关键是思路。

我开始的想法是将每个文字用span标签包裹起来, 给每个span加动画即可。后来发现这样行不通, 因为每次dom中的innerHTML其实是重新渲染的, 那样的话, 之前的span也会一直有动画, 这不是我们想要的效果。

解决方案:

给每个span加个class实现动画, 但在下个span渲染时清除之前span的class, 这样就实现了只有当前的span有动画了.

我这里直接选择了正则匹配粗暴的将class去掉。大家也可以寻找一种更好的方式解决。

代码如下:

实现效果:

源自:https://juejin.im/post/5ddf55835188257313541581

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。