admin 发布的文章 - 青蓝鱼的博客-没有bug的代码是不完美的
admin 发布的文章 - 青蓝鱼的博客-没有bug的代码是不完美的
终于找到这篇文章了,感谢作者的分享!
https://fishfive.top/index.php/archives/29/
终于找到这篇文章了,感谢作者的分享!
受益匪浅,感谢博主。
该回复疑似异常,已被系统拦截!
1
111
666
# 图片回复
666
学到了
666
hello word
首页
关于
?
归档
留言
统计
导航
更多
github
友链
推荐
百度
搜 索
1
Nginx-Quic重新编译Nginx支持HTTP3
356 阅读
2
Centos7和Centos8网卡配置
269 阅读
3
六种好看的css按钮效果
189 阅读
4
node.js简单的web服务demo
159 阅读
5
JavaScript实现静态图片局部流动效果
156 阅读
默认分类
html
css
JavaScript
React
Vue
Git
centos
node.js
php
nginx
http
登录
搜 索
https://fishfive.top
累计撰写
27
篇文章
累计收到
16
条评论
首页
栏目
默认分类
html
css
JavaScript
React
Vue
Git
centos
node.js
php
nginx
http
页面
关于
归档
留言
统计
导航
github
友链
推荐
百度
用户登录
登录
找到
27
篇与
admin
相关的结果
2024-06-12
Artalk表情包
表情名称链接包含下列所有表情https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/main.json除小鸡以外所有表情https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/main1.json颜表情https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/default1.jsonEmojihttps://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/default3.json滑稽https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/hj.json狗头https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/gt.json黄豆https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/hd.json小鸡https://gcore.jsdelivr.net/gh/gitt3/hd@6.3.1/gt/xhj.json效果图:
2024年06月12日
120 阅读
0 评论
0 点赞
2022-12-03
六种好看的css按钮效果
演示html代码<div class="col"> <div class="container-1"> <div class="btn btn-one"> <span>BUTTON ONE</span> </div> </div> <div class="container-2"> <div class="btn btn-two"> <span>IDLE</span> </div> </div> <div class="container-3"> <div class="btn btn-three"> <span>BUTTON THREE</span> </div> </div> </div> <div class="col"> <div class="container-4"> <div class="btn btn-four"> <span>BUTTON FOUR</span> </div> </div> <div class="container-5"> <div class="btn btn-five"> <span>BUTTON FIVE</span> </div> </div> <div class="container-6"> <div class="btn btn-six"> <span>BUTTON SIX</span> </div> </div> </div>css代码 <style> html, body { height: 100%; width: 100%; margin: 0; display: flex; flex-direction: column; flex-wrap: wrap; font-family: 'Open Sans Condensed', sans-serif; } .col { height: 100%; } div[class*=container] { text-align: center; width: 100%; height: 33%; display: flex; justify-content: center; align-items: center; } .container-1 { background: #333; } .container-2 { background: #4E598C; } .container-3 { background: #8499B1; } .container-4 { background: #9888A5; } .container-5 { background: #7B6D8D; } .container-6 { background: #565554; } /* BUTTON STYLING */ .btn { position: relative; color: white; width: 256px; height: 64px; line-height: 64px; transition: all 0.3s; } .btn span { transition: all 0.3s; tranform: scale(1, 1); } .btn::before, .btn::after { content: ''; position: absolute; transition: all 0.3s; bottom: 0; left: 0; width: 100%; height: 100%; z-index: 1; } .btn-one::before { left: 4px; z-index: 1; opacity: 0; background: rgba(255, 255, 255, 0.1); transform: scale(0.1, 1); } .btn-one:hover::before { opacity: 1; transform: scale(1, 1); } .btn-one::after { transition: all 0.3s; border: 1px solid rgba(255, 255, 255, 0.5); } .btn-one:hover::after { transform: scale(1, 0.1); opacity: 0; } /* BTN TWO */ .btn-two::before, .btn-two::after { content: ''; position: absolute; width: 100%; height: 100%; bottom: 0; left: 0; z-index: 1; transition: all 0.3s; border: 1px solid rgba(255, 255, 255, 0.5); } .btn-two:hover::after { animation-name: rotatecw; animation-duration: 2s; } .btn-two:hover::before { animation-name: rotateccw; animation-duration: 3s; } .btn-two:hover::after, .btn-two:hover::before { left: 96px; width: 64px; animation-iteration-count: infinite; animation-timing-function: linear; } @keyframes rotatecw { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes rotateccw { from { transform: rotate(0deg); } to { transform: rotate(-360deg); } } /* BTN THREE */ .btn-three::before { opacity: 0; background: rgba(255, 255, 255, 0.1); transform: scale(1, 0.1); } .btn-three:hover::before { opacity: 1; transform: scale(1, 1); } .btn-three::after { transition: all 0.3s; border: 1px solid rgba(255, 255, 255, 0.5); } .btn-three:hover::after { transform: scale(1, 0.1); opacity: 0; } /* BTN FOUR */ .btn-four:hover span { transform: scale(1.2, 1.2); } .btn-four::before { opacity: 0; background: rgba(255, 255, 255, 0.1); transform: scale(0.1, 0.1); } .btn-four:hover::before { opacity: 1; transform: scale(1, 1); } .btn-four::after { transition: all 0.3s; border: 1px solid rgba(255, 255, 255, 0.5); } .btn-four:hover::after { transform: scale(0, 0); opacity: 0; } /* BTN FIVE */ .btn-five::before { transition: transform 0.15s, 0.15s border-radius 0.15s; opacity: 0; background: rgba(255, 255, 255, 0.1); transform: scale(0.1, 0.1); border-radius: 50%; } .btn-five:hover::before { opacity: 1; transform: scale(1, 1); border-radius: 0; } .btn-five::after { transition: all 0.3s; border: 1px solid rgba(255, 255, 255, 0.3); } .btn-five:hover::after { opacity: 0; } /* BTN SIX */ .btn-six::before { opacity: 0; background: rgba(255, 255, 255, 0.1); transform: scale(1.3, 1.3); } .btn-six:hover::before { opacity: 1; transform: scale(1, 1); } .btn-six::after { transition: all 0.3s; border: 1px solid rgba(255, 255, 255, 0.5); } .btn-six:hover::after { transform: scale(0, 0); opacity: 0; } </style>
2022年12月03日
189 阅读
0 评论
0 点赞
2022-12-02
网站添加 pwa 支持
什么是PWA(渐进式 Web 应用)PWA(Progressive Web Apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。这些应用无处不在、功能丰富,使其具有与原生应用相同的用户体验优势。PWA 是可被发现、易安装、可链接、独立于网络、渐进式、可重用、响应性和安全的。简单理解,PWA可以将Web应用转化成能够在多平台设备上使用的原生应用程序并且具有和原生应用接近的交互体验,无需下载直接安装。注意:PWA并不是一种单独的技术,而是多项Web技术集成的Web App,其核心技术包括App Manifest、Service Worker、Web Push等。pwa优势它比原生应用更轻量,但是却比现有的 Web APP 的功能更加丰富。最大也是最关键的区别是它能够脱离浏览器的「束缚」(虽然依然是基于浏览器的技术),能够把 PWA 网站添加到你的桌面上,不管是 PC 操作系统还是手机操作系统,类似于一个原生应用一样,并且拥有媲美原生应用的体验。而微信支付宝等小程序更封闭,是 Web 的子集。快速为网站配置PWA开启HTTPS,并且强制HTTPS根目录上传 Manifest.JSON 并修改内容根目录上传 Service-Worker.JS注册Service Worker配置ManifestManifest是一份用JSON记录网站信息的清单,其中包含了应用的标题、图标的大小与路径、加载页的背景颜色等,只有编写了这份清单浏览器才能正确处理Web App信息。在编写Manifest时会发现有不同拓展名,如manifest.webapp、manifest.json以下是我编写的一份Manifest示例:{ "background_color": "#fff", //Web App启动时的背景颜色,可以使用预设的一些颜色名字如red,blue等,或者直接使用HEX颜色值如#66ccff(天依蓝) "description": "Describe your site.", //Web App的描述,这里的内容将会在安装时展现 "display": "fullscreen", //Web App展示的方式,可选的值有fullscreen,standalone,minimal-ui,browser等,具体含义请见文末拓展阅读 "icons": [ { "src": "icon.png", "sizes": "192x192", "type": "image/png" } ], //这里举例了应用图片的配置,可以配置多个图标! "name": "Awesome fox pictures", //应用的名称,在安装时展示 "short_name": "Foxes", //缩略名,一般在添加到桌面之后会展示这个名字 "start_url": "/" //启动后进入的 }然后将本文件保存为manifest.json并放置在网站根目录,并在网站标签内添加以下内容:<link rel="manifest" href="manifest.webmanifest">Safari浏览器对PWA支持并不完善,如果需要正确的展示PWA应用,需要将以下代码根据实际情况调整后添加在标签内:<link rel="shortcut icon" href="favicon.ico"> <link rel="shortcut icon" href="src to icon" type="image/x-icon" /> <link rel="apple-touch-icon" href="src to icon" /> <meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-title" content="Web App Name">配置Service Worker接下来是Service Worker的代码,将其命名为sw.js并保存。// sw.js var cacheStorageKey = 'poem-0.0.2' var cacheList = [] self.addEventListener('install', e => { // install 事件,它发生在浏览器安装并注册 Service Worker 时 // e.waitUtil 用于在安装成功之前执行一些预装逻辑 e.waitUntil( caches.open(cacheStorageKey) .then(cache => cache.addAll(cacheList)) .then(() => self.skipWaiting()) ) }) self.addEventListener('fetch', function(e) { e.respondWith( caches.match(e.request).then(function(response) { if (response != null) { return response } return fetch(e.request.url) }) ) }) self.addEventListener('activated', function(e) { e.waitUntil( // 获取所有cache名称 caches.keys().then(cacheNames => { return Promise.all( // 获取所有不同于当前版本名称cache下的内容 cacheNames.filter(cacheNames => { return cacheNames !== cacheStorageKey }).map(cacheNames => { return caches.delete(cacheNames) }) ) }).then(() => { return self.clients.claim() }) ) })sw.js 的内容基本是从网上拷贝的。这个文件会运行在 service worker 线程中,并且会拦截各种网络请求。文件中监听的 fetch 事件回调就是对请求进行的处理。我这里会尝试从缓存获取,如果缓存没有才发起请求。在一开始,我将跟路径“/”添加到了 cacheList 中。其结果是首页被缓存,导致后续发版无法更新。还好当时是本地测试,在浏览器开发工具 application 面板清除缓存并将 cacheList 置空后,问题迎刃而解。当然,这种做法并不好,实际上可以通过检测缓存版本解决。不过,算了,我只是需要将网站添加到桌面。对于只需要添加桌面图标,不需要缓存内容,我找到了更简单的版本。const cacheName = 'kodcloud'; const staticAssets = []; self.addEventListener('install', async e => { }); self.addEventListener('activate', e => { self.clients.claim(); }); self.addEventListener('fetch', async e => { });注册Service Worker首先编写index.js,这部分内容也可以直接编写在网页的<script>标签中。 if(navigator.serviceWorker != null){ navigator.serviceWorker.register('sw.js',) .then(function(registartion){ //注册成功 console.log('支持sw:',registartion.scope) }).catch(function (err){ console.log('fail') }); }至此,大功告成。
2022年12月02日
85 阅读
0 评论
0 点赞
2022-11-09
node.js简单的web服务demo
nodejs简单编写 http 服务程序步骤:1加载http模块。2创建http服务.3为http服务对象添加 request 事件处理程序。4开启http服务监听,准备接收客户端请求。注意:浏览器显示可能是乱码,所以可以通过 res.setHeader('Content-Type', 'text/plain; charset=utf-8');设置浏览器显示时所使用的编码。Chrome 浏览器默认无法手动设置编码,需要安装"Set Character Encoding"扩展。演示一下设置Content-Type=text/html 和 Content-Type=text/plain的区别。// 1. 加载http模块 var http = require('http'); // 2. 创建http服务 var server = http.createServer(); // 3. 开始监听'request'事件 // 详细解释一下request对象和response对象 server.on('request', function (req, res) { // body... console.log('有人请求了~~'); }); // 4. 启动服务,开始监听 server.listen(9000, function () { console.log('服务已经启动,请访问: http://localhost:9000'); });编写 http 服务程序 - 通过读取静态 HTML 文件来响应用户请求步骤:1创建index.html、login.html、register.html、list.html、404.html文件。 2演示通过读取最简单的 HTML 文件来响应用户。3演示通过读取"具有引入外部CSS样式表"的HTML文件来响应用户。4演示通过读取"具有img标签"的HTML文件来响应用户。注意:1、注意在发送不同类型的文件时,要设置好对应的Content-Type Content-Type参考: http://tool.oschina.net/commonsContent-Type参考: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types 2、HTTP状态码参考 w3org参考:https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html w3schools参考: https://www.w3schools.com/tags/ref_httpmessages.asp 3、在html页面中写相对路径'./' 和 绝对路径 '/'的含义 。 网页中的这个路径主要是告诉浏览器向哪个地址发起请求用的'./' 表示本次请求从相对于当前页面的请求路径(即服务器返回当前页面时的请求路径)开始 '/' 表示请求从根目录开始补充知识点: path 模块的 join() 方法// 1. 加载 http 模块 var http = require('http'); // 加载文件操作模块 var fs = require('fs'); // 加载path模块,这个模块主要用来处理各种路径。 var path = require('path'); // 2. 创建http server var server = http.createServer(function (req, res) { // 1. 获取用户请求的URL var url = req.url.toLowerCase(); // 2. 根据用户的不同请求,做出不同响应 if (url === '/' || url === '/index') { // 读取index.html文件,把该文件响应给用户 fs.readFile(path.join(__dirname, 'index.html'), function (err, data) { if (err) { throw err; } res.writeHead(200, 'OK', { 'Content-Type': 'text/html; charset=utf-8' }); // res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end(data); }); } else if (url === '/login') { // 读取login.html文件,把该文件响应给用户 fs.readFile(path.join(__dirname, 'login.html'), function (err, data) { if (err) { throw err; } res.writeHead(200, 'OK', { 'Content-Type': 'text/html; charset=utf-8' }); // res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end(data); }); } else if (url === '/register') { // 读取register.html文件,把该文件响应给用户 fs.readFile(path.join(__dirname, 'register.html'), function (err, data) { if (err) { throw err; } res.writeHead(200, 'OK', { 'Content-Type': 'text/html; charset=utf-8' }); // res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end(data); }); } else if (url === '/404') { // 读取register.html文件,把该文件响应给用户 fs.readFile(path.join(__dirname, '404.html'), function (err, data) { if (err) { throw err; } res.writeHead(200, 'OK', { 'Content-Type': 'text/html; charset=utf-8' }); // res.setHeader('Content-Type', 'text/html; charset=utf-8'); res.end(data); }); } }); // 3. 启动服务 server.listen(9090, function () { // body... console.log('please visit: http://localhost:9090'); });模拟 Apache 实现静态资源服务器步骤:单独创建一个目录来实现,比如:创建一个"07-Apache"的目录。在该目录下新建 public 目录,假设该目录为静态资源目录。根据用户请求的路径在 public 目录下寻找对应路径下的资源。如果找到了,那么将该资源返回给用户,如果没找到则返回404错误。通过 mime 模块设置不同类型资源的Content-Type实现完毕后把素材中的'An Ocean of Sky' 和 'Hacker News'分别拷贝到静态资源目录下, 测试是否成功其他:介绍 NPM介绍 mime 第三方模块npm install mime在代码中直接var mime = require('mime')// 1. 加载对应模块 // 1.1 加载http模块 var http = require('http'); // 1.2 加载path模块,方便路径拼接 var path = require('path'); // 1.3 加载文件读取模块 var fs = require('fs'); // 1.4 加载判断文件MIME类型的模块 var mime = require('mime'); // 2. 创建http server var server = http.createServer(); // 3. 监听用户request事件 server.on('request', function (req, res) { // 1. 获取用户的请求路径, 并转换为小写 var url = req.url.toLowerCase(); // 判断如果请求的路径是 '/' 那么等价于 '/index.html' url = (url === '/') ? '/index.html' : url; // 2. 根据用户请求的url路径, 去public目录下查找对应的静态资源文件。找到后读取该文件,并将结果返回给用户 // 2.1 根据用户请求的url拼接本地资源文件的路径 var filePath = path.join(__dirname, 'public', url); // 2.2 根据请求的文件路径设置Content-Type res.setHeader('Content-Type', mime.lookup(url)); // 2.2 根据路径去读取对应的文件 // 【注意】读取文件前无需判断文件是否已经存在,而是在读取文件的回调函数中根据error的错误信息来判断读取文件是否成功以及发生的错误 fs.readFile(filePath, function (err, data) { // 判断是否有错误 if (err) { if (err.code === 'ENOENT') { // 判断是否是请求的文件是否不存在 res.setHeader('Content-Type', 'text/html; charset=utf8'); res.statusCode = 404; res.statusMessage = 'Not Found'; res.end('<h1>请求的资源不存在!</h1>'); } else if (err.code === 'EACCES') { // 判断文件是否有访问权限 res.setHeader('Content-Type', 'text/html; charset=utf8'); res.statusCode = 403; res.statusMessage = 'Forbidden'; res.end('<h1>Permission denied!</h1>'); } else { throw err; } } else { // 如果没有错误则将读取到的文件返回给用户 res.statusCode = 200; res.statusMessage = 'OK'; res.end(data); } }) }); // 4. 启动服务 server.listen(9000, function () { // body... console.log('server is running, please visit: http://localhost:9000'); });Common System Errors - 常见错误号EACCES (Permission denied)An attempt was made to access a file in a way forbidden by its file access permissions.访问被拒绝EADDRINUSE (Address already in use)An attempt to bind a server (net, http, or https) to a local address failed due to another server on the local system already occupying that address.地址正在被使用(比如:端口号备占用)EEXIST (File exists)An existing file was the target of an operation that required that the target not exist.文件已经存在EISDIR (Is a directory)An operation expected a file, but the given pathname was a directory.给定的路径是目录ENOENT (No such file or directory)Commonly raised by fs operations to indicate that a component of the specified pathname does not exist -- no entity (file or directory) could be found by the given path.文件 或 目录不存在ENOTDIR (Not a directory)A component of the given pathname existed, but was not a directory as expected. Commonly raised by fs.readdir.给定的路径不是目录设置 http 响应报文头实现弹框下载功能设置 Content-Type: application/octet-stream设置 Content-Disposition: attachment; filename=demo.txtvar http = require('http'); var server = http.createServer(function (req, res) { // body... res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Content-Disposition': 'attachment; filename=demo.txt' }); res.end('hello node.js'); }); server.listen(9000, function () { // body... console.log('server is running...'); });
2022年11月09日
159 阅读
0 评论
0 点赞
2022-11-08
JavaScript实现静态图片局部流动效果
背景如果你有玩过《王者荣耀》、《阴阳师》 等手游,一定注意到过它的启动动画、皮肤立绘卡片等场景,经常采用静态底图加局部液态流动效果的简单动画,这些流动动画可能出现在缓缓流动的水流 、迎风飘动的旗帜 、游戏角色衣袖 、随着时间缓动的云、雨、雾天气效果 等。这种过渡效果不仅节省了开发全量动画的成本,而且使得游戏画面更加热血、冒险、奥德赛、高级,也更加容易吸引玩家氪金 。本文使用前端开发技术,结合 SVG 和 CSS 来实现类似的液化流动效果。本文包含的知识点主要包括:mask-image 遮罩、feTurbulence 和 feDisplacementMap 滤镜、filter 属性、canvas 绘制方法、TimelineMax 动画以及input[type=file] 本地图片资源加载等。效果先来看看实现效果,下面几个示例以及文章 Banner 图都是应用了由本文内容生成的液态流动动画效果。由于GIF 图压缩比较严重,动画效果看起来不是很流畅。雾气扩散 塞尔达传说:旷野之息衣袖飘动 貂蝉:猫影幻舞湖光波动实现页面主要由2部分构成,顶部用于加载图片 ,并且可以通过按住鼠标划动的方式绘制热点路径,给图片添加流动效果;底部是控制区域,点击按钮清除画布,可以清除绘制的流动动画效果、点击按钮切换图片可以加载本地的图片。HTML 页面结构\#sketch 元素主要是用于绘制和加载流动效果热点图的画板;\#button_container 是页面底部的按钮控制区域;svg 元素用于利用其 filter 滤镜实现液态流动动画效果,包括 feTurbulence 和 feDisplacementMap 滤镜。<main id="sketch"> <canvas id="canvas" data-img=""></canvas> <div class="mask"> <div id="maskInner" class="mask-inner"></div> </div> </main> <section class="button_container"> <button class="button">清除画布</button> <button class="button"><input class="input" type="file" id="upload">上传图片</button> </section> <svg> <filter id="heat" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%"> <feTurbulence id="heatturb" type="fractalNoise" numOctaves="1" seed="2" /> <feDisplacementMap xChannelSelector="G" yChannelSelector="B" scale="22" in="SourceGraphic" /> </filter> </svg> feTurbulence 和 feDisplacementMapfeTurbulence:滤镜利用 Perlin 噪声函数创建了一个图像,利用它可以实现人造纹理比如说云纹、大理石纹等模拟滤镜效果。feDisplacementMap:映射置换滤镜,该滤镜用来自图像中从 in2 到空间的像素值置换图像从 in 到空间的像素值。即它可以改变元素和图形的像素位置,通过遍历原图形的所有像素点,feDisplacementMap 重新映射替换一个新的位置,形成一个新的图形。该滤镜在业界的主流应用是对图形进行形变,扭曲,液化。CSS 样式接着看看样式的实现,main 元素作为主容器并将主图案作为背景图片;canvas 作为画布占据 100% 的空间位置;.mask 和 .mask-inner 用于生成如下图所示热点路径与背景图相溶的效果,这种效果是借助 mask-image 实现的。最后,为了生成动态流动效果,.mask-inner 通过 filter: url(#heat) 将前面生成的 svg 作为滤镜来源,后续即将在 JavaScript 中通过不间断修改 svg 滤镜的属性,来生成液态流动动画。main { position: relative; background-image: url('bg.jpg'); background-size: cover; background-position: 100% 50%; } canvas { opacity: 0; position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .mask { display: none; position: absolute; top: 0; left: 0; width: 100%; height: 100%; mask-mode: luminance; mask-size: 100% 100%; backdrop-filter: hard-light; mask-image: url('mask.png'); } .mask-inner { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: url('bg.jpg') 0% 0% repeat; background-size: cover; background-position: 100% 50%; filter: url(#heat); mask-image: url('mask.png') } mask-imagemask-image CSS 属性用于设置元素上遮罩层的图像。语法:// 默认值,透明的黑色图像层,也就是没有遮罩层。 mask-image: none; // <mask-source><mask>或CSS图像的url的值 mask-image: url(masks.svg#mask1); // <image> 图片作为遮罩层 mask-image: linear-gradient(rgba(0, 0, 0, 1.0), transparent); mask-image: image(url(mask.png), skyblue); // 多个值 mask-image: image(url(mask.png), skyblue), linear-gradient(rgba(0, 0, 0, 1.0), transparent); // 全局值 mask-image: inherit; mask-image: initial; mask-image: unset; 兼容性:JavaScript 方法① 绘制热点图监听鼠标移动和点击事件,在 canvas 上绘制波动路径热点。var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var sketch = document.getElementById('sketch'); var sketchStyle = window.getComputedStyle(sketch); var mouse = ; canvas.width = parseInt(sketchStyle.getPropertyValue('width')); canvas.height = parseInt(sketchStyle.getPropertyValue('height')); canvas.addEventListener('mousemove', e => { mouse.x = e.pageX - canvas.getBoundingClientRect().left; mouse.y = e.pageY - canvas.getBoundingClientRect().top; }, false); ctx.lineWidth = 40; ctx.lineJoin = 'round'; ctx.lineCap = 'round'; ctx.strokeStyle = 'black'; canvas.addEventListener('mousedown', () => { ctx.beginPath(); ctx.moveTo(mouse.x, mouse.y); canvas.addEventListener('mousemove', onPaint, false); }, false); canvas.addEventListener('mouseup', () => { canvas.removeEventListener('mousemove', onPaint, false); }, false); var onPaint = () => { ctx.lineTo(mouse.x, mouse.y); ctx.stroke(); var url = canvas.toDataURL(); document.querySelectorAll('div').forEach(item => { item.style.cssText += ` display: initial; -webkit-mask-image: url($); mask-image: url($); `; }); }; 绘制完成后,可以在页面中右键保存生成的波动路径热点图,直接将绘制满意的热点图放到 CSS 中,就能给喜欢的图片添加局部波动效果了,下面这张图片就是本示例页面使用的波动的热点路径图。② 生成动画为了生成实时更新的波动效果,本文使用了 TweenMax 来通过改变 feTurbulence 的 baseFrequency 属性值来实现,使用其他动画库或使用 requestAnimationFrame 也是可以实现相同的功能。feTurb = document.querySelector('#heatturb'); var timeline = new TimelineMax({ repeat: -1, yoyo: true }), timeline.add( new TweenMax.to(feTurb, 8, { onUpdate: () => { var bfX = this.progress() * 0.01 + 0.025, bfY = this.progress() * 0.003 + 0.01, bfStr = bfX.toString() + ' ' + bfY.toString(); feTurb.setAttribute('baseFrequency', bfStr); } }), 0); ③ 清除画布点击清除画布按钮,可以清空已经绘制的波动路径,主要是通过清除页面元素 mask-image 的属性值以及清 canvas 画布来实现的。function clear() { document.querySelectorAll('div').forEach(item => { item.style.cssText += ` display: none; -webkit-mask-image: none; mask-image: none; `; }); } document.querySelectorAll('.button').forEach(item => { item.addEventListener('click', () => { ctx.clearRect(0, 0, canvas.width, canvas.height); clear(); }) }); ④ 切换图片点击切换图片,可以加载本地的一张图片作为绘制底图,该功能是通过 input[type=file] 来实现图片资源的获取,然后通过修改 CSS 将它设置成新的画布背景。document.getElementById('upload').onchange = function () { var imageFile = this.files[0]; var newImg = window.URL.createObjectURL(imageFile); clear(); document.getElementById('sketch').style.cssText += ` background: url($); background-size: cover; background-position: center; `; document.getElementById('maskInner').style.cssText += ` background: url($); background-size: cover; background-position: center; `; };到这里,全部功能都实现完毕了,大家赶快制作一张自己喜欢的 史诗皮肤 或 奥德赛小游戏 的启动页面吧。总结mask-image 遮罩元素feTurbulence 和 feDisplacementMap svg滤镜filter 属性Canvas 绘制方法TimelineMax 动画input[type=file] 本地图片资源加载
2022年11月08日
156 阅读
0 评论
0 点赞
1
2
...
6