突然对图片拖动这样验证方式感兴趣,简单写了一个 demo 出来。但是光写这个感觉太单调,然后就加上了Web Component,简单了解了一下这个原生组件的方案。
效果
稍微有点简陋😂
Web Component
原生的组件化方案,具体就不多说了,详细可以查看 MDN,本次也只是简单了解了一下,上图整个效果都是封装在 img-validator 中(只是效果,相关属性事件都没写)。
<!-- 引入后直接使用 -->
<img-validator></img-validator>
渲染出来是这样的:
想要自定义组件,需要使用 customElements.define(ElementName, CustomElementClass, options); 来进行定义,其中 ElementName 必须使用短横线,CustomElementClass 是用来实例化 dom 对象的,理论上使用构造函数的方式也行,options 是一些其他选项。
自定义元素的类里面直接像写普通类就行,没啥特别的限制,开发时可以直接继承现有的节点类,可以减少很多工作量,如果从 0 开始写的话就需要考虑 slots、属性以及事件等细节。
// 必须继承 HTMLElement 或者其他更明确的节点
class CustomEl extends HTMLElement {
/// ....
constructor() {
super();
// 对于属性的处理,可以利用 hasAttribute 来判断,然后直接 setAttribute 到任何想要设置的节点上
// 这样在自定义组件上的属性才会生效
if (this.hasAttribute('style')) {
wrapper.setAttribute('style', this.getAttribute('style'));
}
}
}
// 然后就可以直接 html 中使用了
customElements.define('custom-el', CustomEl);
事件这次没涉及到,可以利用自定义事件和
this.dispatchEvent()来触发
继承类似 HTMLUListElement 的节点时,在使用时有些许不同
<!-- 继承 HTMLElement 时 -->
<custom-el></custom-el>
<!-- 继承 HTMLUListElement 时 -->
<ul is="custom-el"></ul>
需要注意的是,这种处于
shadow-root中的元素在外部不能直接获取到,必须先获取到宿主节点,比如img-validator,然后利用宿主上的shadowRoot获取到组件的根节点(必须是处于 open mode 才能拿到),然后就可以在这个节点上使用选择器查找 dom 节点。
组件内部的 dom 结构可以自己一个一个节点创建,也可以简单粗暴直接
root.innerHTML = '<div>23333</div>',还可以利用template,template不会在页面上渲染出来,这样就可以在实例化自定义组件时通过选择器获取到模板节点,然后直接插入。
周期钩子
-
connectedCallback()首次插入文档中后被调用,本次用来初始化事件监听 -
disconnectedCallback()被删除后调用,本次用来移出事件监听器
还有两个请参考 MDN 自定义元素生命周期
滑动校验
首先我们需要先把图片展示出来,其次需要一个不会移动的目标区域,还要有一个可以移动的区域,所以整体的 dom 结构就确定下来了
<div class="img-wrapper">
<img src id="img" />
<div id="moveTicket"></div>
<div id="targetTicket"></div>
</div>
<!-- 滑块 -->
<div class="slider">
<div class="slider-line"></div>
<div class="slider-handle" id="slider-handle"></div>
</div>
拖动就不说了,就监听鼠标事件就行,但是这样会存在一些体验问题,暂时还没想好怎么解决,就是在快速滑动时,容易卡在一个地方动不了,不过这不是重点,我们慢慢拖动就行
在图片加载完成后,会随机初始化一下目标区域(即 #targetTicket)的位置(这个应该需要后端生成比较合理),然后给移动区域(#moveTicket)添加背景图,这主要是为了营造出碎片的错觉,利用背景图片的定位(只需要将目标区域的坐标取相反数然后提供给背景图就行,即目标区域坐标 (x, y),背景图定位 el.style.backgroundPosition = -${x}px -${y}px 就可以找到对应区域了)
clip-path
闲着没事为了更加贴近实际的验证,特意添加了这种拼图效果
这是使用 css 中的 clip-path 实现的
clip-path: path('M 0 0 v 30 A 7.5 7.5, 0, 1, 1, 0 45 v 15 h 15 A 7.5 7.5, 0, 1, 1, 30 60 h 30 v -60 z');
手写这个路径真痛苦又学到了新知识,clip-path 可以使用和 svg 中的 path 一样的语法来创建复杂的图形,这里就不过多介绍了。
验证
这里使用了偷懒的假验证,直接判断移动区域的 offsetLeft 是不是在预期的区间中,是的话就通过验证,实际上应该将位置交给后端来验证更为合理,当然这里就不需要了。
结尾
总体实现都比较简陋,只是满足了一下自己的好奇心。也体会到现代前端框架的几个优点,组件的 html 模板代码都可以非常简单快捷的编写,事件也不需要自己操心,针不戳