刮乐
分享一段用canvas和JS制作刮刮乐的代码,JS部分去掉注释不到20行代码
效果图
HTML
CSS
首先 需要绘制一块Canvas蒙版 遮罩在图片上
绘制蒙版
效果图
然后利用canvas的globalCompositeOperation属性 显示原来的不在后来区域的部分
消除蒙版
效果图
那么会出现一个问题 用户需要刮开全部蒙版的话会很费事(强迫症伤不起 - -), 所以再加一段代码
添加刮开70% 自动刮开全部效果
整体代码
最终效果
本demo主要运用到globalCompositeOperation 画布的一个功能 作用是设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上,还有其余10种写法
https://jsfiddle.net/jmogkq9d/3/
刮卡是大家非常熟悉的一种网页交互元素了。实现刮涂层的效果,需要借助canvas来实现,想必每个前端工程师都清楚。实现刮刮卡并不难,但其中却涉及很多知识点,掌握这些知识点,有助于我们更深刻理解原理,对于提升举一反三的能力很有帮助。本期以实现刮刮卡为例,分享下如何科学合理地封装函数,并对涉及的相关知识点进行讲解。
先看下最终效果:
实现刮刮卡都涉及到哪些知识点呢?
下面进入本期分享的正式内容。
为了满足更多的场景需要,我们尽可能地提供更多的参数,方便使用者。先从产品和UI的角度来思考下,一个刮刮卡可能需要哪些配置选项。
接下来再补充下技术配置选项:
OK,确认好以上配置参数后,就可以正式开工了。
项目目录结构如下:
页面结构很简单,div的background显示结果,div里的canvas用来做涂层。
新建index.html,加入以下代码(HTML模板代码略过):
HTML代码:
CSS代码:
award.jpg用的是2倍图,因此使用 background-size缩放回1倍显示大小。
这里可以发现,HTML中canvas的width、height与CSS中的width、height不一致。原因就是要适应Retina 2倍屏幕。这里就涉及到了canvas画布尺寸的知识点。
现在页面显示效果如下,结果图像已显示出来:
知识点1:canvas元素尺寸与画布尺寸
HTML中canvas的width、height是画布大小,通俗来讲就是canvas画布的“绘制区域大小”,一定要跟元素的显示大小区别开来。
我们的结果图素材是750x280,所以要让canvas完全绘制这张图片,画布大小也需要是750x280。
那么元素大小,就是canvas在页面的“显示大小”。通过CSS对canvas元素进行宽高设置,使其正确的显示。
新建scratchcard.js。
结合第1章节的需求分析,类的雏形如下:
使用对象的方式向函数传参有很多优点:
使用Object.assign方法,可将传递进来的config参数覆盖默认参数。传递的config中没有的属性,则使用默认配置。
在index.html中引入scratchcard.js,在body最下边插入script代码:
刮刮卡的类使用起来非常方便,仅传递不使用默认配置的值即可。
4.1 构建ScratchCard原型
继续编写scratchcard.js:
这里设置了constructor: ScratchCard,仅仅是为了显得更加严谨,省略这一行也是没有问题的。
由代码中 prototype 和 constructor 引出第2个知识点。
知识点2:prototype、__proto__、constructor
先记住两点:
※由于JS中函数也是一种对象,所以函数也拥有__proto__和constructor属性。
【__proto__】
__proto__属性都是由一个对象指向一个对象,即指向它们的原型对象(也可以理解为父对象)。
它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,如果父对象也不存在这个属性,则继续在父对象的__proto__属性所指向的对象(爷爷对象)里找,如果还没找到,则继续往上找,直到原型链顶端null。null为原型链的终点。
由以上这种通过__proto__属性来连接对象直到null的一条链即为所谓的原型链。
【prototype】
prototype对象是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是由这个函数所创建的实例的原型对象。
因此,以上代码中,demo.__proto__===Demo.prototype。
prototype属性的作用就是:prototype包含的属性和方法可被其创建的全部实例所共用。
【constructor】
constructor属性也是对象独有的,它是从一个对象指向一个函数。其含义就是指向该对象的构造函数。所有函数最终的构造函数都指向Function。
当创建一个函数的时候,会同时自动创建它的prototype对象,这个对象也会自动获得constructor属性,并指向自己。
那么,为什么我们这里还要手动设置constructor: ScratchCard呢?
原因就是我们用这样的语法:
会导致自动设置的constructor属性值被覆盖。在这种情况下,如果我们不特意设置constructor: ScratchCard的话,constructor则会指向Object。
4.2 实现canvas涂层
先添加以下代码:
初始化代码就是实现涂层的覆盖。这里的关键逻辑是:如果设置了图像涂层,则忽略纯色涂层。
涉及到了canvas两个API:
drawImage 用于绘制图像。
fillRect 用于绘制矩形,在绘制之前要先设置笔刷,即通过fillStyle属性设置颜色。
这段代码是什么意思呢?
globalCompositeOperation就是第3个知识点。
知识点3:canvas的globalCompositeOperation
在w3school上可以查阅到该属性的详细说明:
看上去好像有点懵逼难理解,其实就是类似于指定photoshop里两个图层怎么融合,比如谁遮罩谁、交叉部分消除、交叉部分颜色融合等等。
可以参看下w3school的图示,蓝色为目标图像,红色为源图像。
回到刮刮卡,图片涂层是目标图像,目前源图像还未设置,所以源图像为全透明(源图像的不透明的部分用来抠除目标图像并呈现透明),所以目标图像(图片涂层)全部显示。
现在效果如下图所示,涂层已经覆盖上了。
4.3 添加涂抹事件
涂抹事件,其实就是用touchstart、touchmove、touchend事件,为了顺便兼容鼠标操作,也把mousedown、mousemove、mouseup带上。
修改代码:
代码很好理解,就是添加事件监听。当按下的时候,把isDown设置为true,当抬起的时候,把isDown设置为false。
可以看到addEventListener的第3个参数{ passive: false },这是个什么鬼?这就是第4个知识点。
知识点4:addEventListener第三个参数的passive属性
最开始,addEventListener() 的参数约定是这样的:
三个属性的默认值都为 false。
为什么会多出个passive属性呢?
为了防止页面滚动,很多移动端页面都会监听 touchmove 等 touch 事件,像这样:
由于 touchmove 事件对象的 cancelable 属性为 true,也就是说它的默认行为可以被监听器通过 preventDefault() 方法阻止。那它的默认行为是什么呢,通常来说就是滚动当前页面(还可能是缩放页面),如果它的默认行为被阻止了,页面就必须静止不动。但浏览器无法预先知道一个监听器会不会调用 preventDefault(),它能做的只有等监听器执行完后再去执行默认行为,而监听器执行是要耗时的,有些甚至耗时很明显,这样就会导致页面卡顿。即便监听器是个空函数,也会产生一定的卡顿,毕竟空函数的执行也会耗时。
当设置了passtive为true,则会忽略代码中的preventDefault(), 因此页面会变得更流畅。如下演示,右侧手机的页面设置了passtive为true。
OK,那么问题来了?既然默认是passive: false,为什么代码里还要再多此一举写一遍呢?
答案在这里,来看chrome的官方说明:
https://www.chromestatus.com/feature/5093566007214080
原文如下:
AddEventListenerOptions defaults passive to false. With this change touchstart and touchmove listeners added to the document will default to passive:true (so that calls to preventDefault will be ignored).
意思是:addEventListener的option里,默认passive是false。但是如果事件是 touchstart 或 touchmove的话,passive的默认值则会变成true(所以preventDefault就会被忽略了)。
OK,原理讲完了,我们还没有把页面的默认滑动行为阻止掉。不阻止的话,在滑动刮刮卡的时候,页面也会跟着滚动。
4.4 阻止页面滚动
看完了4.3小节,那么阻止页面滚动就很简单了。在index.html的script里加入以下代码:
4.5 实现擦除效果
这里完善下_scratch方法,代码如下:
逻辑大致如下:
需要说明的是,乘以pixelRatio是为了适应多倍屏幕。在本示例中,画布尺寸是2倍尺寸,而坐标是按照网页元素的尺寸计算出来的,正好相差一倍,所以要乘以pixelRatio(pixelRatio=2)。
还记得4.2小节讲的globalCompositeOperation么?当设置为destination-out的时候,源图像的非透明部分会抠去目标图像,因此实现了刮刮卡的刮涂层效果。
4.6 检测涂层的透明部分占比
虽然刮涂层的效果实现了,但是还要实时检测刮开了多少,来判断是否完成刮刮卡。
继续修改代码:
新增了3个方法:
_scratchAll: 清空涂层(全部刮开)。如果设置的fadeOut(淡出时间),则通过CSS动画,将canvas做淡出效果,然后再清除涂层。如果fadeOut为0,则直接清除涂层。
_clear:清除涂层。很简单,直接画一个铺满画布的矩形即可。
_getFilledPercentage:计算刮开区域的百分比。通过遍历canvas每个像素点,计算全透明像素的占比。
这里就涉及到了第5个知识点。
知识点5:canvas的ImageData
利用canvas的getImageData()方法可以获取到全部的像素点信息,返回数组格式。数组中,并不是每个元素代表一个像素的信息,而是每4个元素为一个像素的信息。例如:
data[0]=像素1的R值,红色(0-255)
data[1]=像素1的G值,绿色(0-255)
data[2]=像素1的B值,蓝色(0-255)
data[3]=像素1的A值,alpha 通道(0-255; 0 透明,255完全可见)
data[4]=像素2的R值,红色(0-255)
...
本例的透明度不存在中间值,所以就可以认为alpha小于128即为透明。
4.7 注意事项
由于浏览器安全限制,Image不能读取本地图片,因此需要部署在服务端,以http协议浏览本项目。
以上就是本期分享的全部内容了。完整代码请前往GitHub:
https://github.com/Yuezi32/scratchcard
看似简单的刮刮卡却隐藏了这么多的知识点,你都掌握了么?
XSS(Cross Site Scripting)攻击全称跨站脚本攻击,为了不与 CSS(Cascading Style Sheets)名词混淆,故将跨站脚本攻击简称为 XSS,XSS 是一种常见 web 安全漏洞,它允许恶意代码植入到提供给其它用户使用的页面中。
引入一下 依赖即可
<!--XSS 安全过滤-->
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>mica-core</artifactId>
<version>2.0.9-GA</version>
</dependency>
<dependency>
<groupId>net.dreamlu</groupId>
<artifactId>mica-xss</artifactId>
<version>2.0.9-GA</version>
</dependency>@GetMapping("/xss")
public String xss(String params){
return params;
}?> ~ curl --location --request GET 'http://localhost:8080/xss?params=%3Cscript%3Ealert(%27xxx%27)%3C/script%3E'@PostMapping("/xss")
public String xss(String params){
return params;
}curl --location --request POST 'http://localhost:8080/xss' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'params=<script>alert('\''xxx'\'')</script>' @PostMapping("/xss")
public String xss(@RequestBody Map<String,String> body){
return body.get("params");
}curl --location --request POST 'http://localhost:8080/xss' \
--header 'Content-Type: application/json' \
--data-raw '{
"params":"<script>alert('\''XXX'\'')</script>"
}'可以使用 @XssCleanIgnore 注解对方法和类级别进行忽略。
@XssCleanIgnore
@PostMapping("/xss")
public String xss(@RequestBody Map<String,String> body){
return body.get("params");
}目前网上大多数的方案如下图,新增 XssFilter 拦截用户提交的参数,进行相关的转义和黑名单排除,完成相关的业务逻辑。在整个过程中最核心的是通过包装用户的原始请求,创建新的 requestwrapper 保证请求流在后边的流程可以重复读。
Spring WebDataBinder 的作用是从 web request 中把 web 请求里的parameters绑定到对应的JavaBean上,在 Controller 方法中的参数类型可以是基本类型,也可以是封装后的普通 Java 类型。若这个普通的 Java 类型没有声明任何注解,则意味着它的每一个属性都需要到 Request 中去查找对应的请求参数,而 WebDataBinder 则可以帮助我们实现从 Request 中取出请求参数并绑定到 JavaBean 中。
SpringMVC 在绑定的过程中提供了用户自定义编辑绑定的接口,注入即可在参数绑定 JavaBean 过程中执行过滤。
在 Spring Boot 中默认是使用 Jackson 进行序列化和反序列化 JSON 数据的,那么除了可以用默认的之外,我们也可以编写自己的 JsonSerializer 和 JsonDeserializer 类,来进行自定义操作。用户提交 JSON 报文会通过 Jackson 的 JsonDeserializer 绑定到 JavaBean 中。我们只需要自定义 JsonDeserializer 即可完成在绑定 JavaBean 中执行过滤。
在 mica-xss 中并未采取上文所述通过自己手写黑名单或者转义方式的实现方案,而是直接实现 Jsoup 这个工具类。
jsoup 实现 WHATWG HTML5 规范,并将 HTML 解析为与现代浏览器相同的 DOM。
*请认真填写需求信息,我们会在24小时内与您取得联系。