WooCommerce-43发布-WP站长

<!-- wp:paragraph --><p>WooCommerce 4.3现已公开公布!它自2020年4月以来始终在开发中,其外围已更新,来自24个贡献者的272次提交。</p><!-- /wp:paragraph --> <!-- wp:paragraph --><p>这是一个主要发行版,这意味着所有内容都与先前版本向后兼容。</p><!-- /wp:paragraph --> <!-- wp:paragraph --><p>与平常一样,建议您创立网站的备份,并确保主题和插件在更新之前兼容。您能够查看此更新指南以理解更多信息。</p><!-- /wp:paragraph --> <!-- wp:heading --><h2>WooCommerce 4.3中有什么新性能?</h2><!-- /wp:heading --> <!-- wp:paragraph --><p>与所有主要版本一样,咱们为您带来了几个咱们要强调的新性能:</p><!-- /wp:paragraph --> <!-- wp:heading {"level":3} --><h3>新首页体验</h3><!-- /wp:heading --> <!-- wp:paragraph --><p>咱们为您提供了一个全新的主页,以提供更加集中的体验,从而使商店治理更加轻松。它仅蕴含3个最重要的我的项目,供商家浏览:</p><!-- /wp:paragraph --> <!-- wp:list --><ul><li>收件箱音讯,</li><li>常见商店指标的疾速概述,以及</li><li>指向最罕用设置的快捷方式</li></ul><!-- /wp:list --> <!-- wp:image {"align":"center","id":6745,"linkDestination":"custom"} --><div class="wp-block-image"><figure class="aligncenter"><img src="https://woocommerce.files.wordpress.com/2020/06/home-screen.png?w=580" alt="WooCommerce 4.3公布!" class="wp-image-6745"/></figure></div><!-- /wp:image --> <!-- wp:paragraph --><p>咱们很快乐在新首页上听到您的反馈和想法,心愿咱们能够将其置于WooCommerce体验的核心地位。</p><!-- /wp:paragraph --> <!-- wp:paragraph --><p>默认状况下,新主页可用于所有新客户。如果要从晚期版本升级,则能够通过WooCommerce>设置>高级>性能>主屏幕将其关上。</p><!-- /wp:paragraph --> <!-- wp:paragraph --><p>您还能够查看波及主屏幕性能的新文档局部。</p><!-- /wp:paragraph --> <!-- wp:heading {"level":3} --><h3>Block块更新</h3><!-- /wp:heading --> ...

July 11, 2020 · 1 min · jiezi

H5之外部浏览器唤起微信分享

最近在做一个手机站,要求点击分享能够间接关上微信分享进来。而不是jiathis,share分享这种的点击进去二维码。在网上看了很多,都说APP能唤起微信,手机网页实现不了。也找了很多都不能间接唤起微信。 总结进去一个能够间接唤起微信的。适应手机qq浏览器和uc浏览器。 上面上代码,把这些间接放到要转发的页面里就能够了: html局部: <script src="mshare.js"></script>//引进mshare.js<button data-mshare="0">点击弹出原生分享面板</button><button data-mshare="1">点击触发朋友圈分享</button><button data-mshare="2">点击触发发送给微信敌人</button>js局部: <script>var mshare = new mShare({ title: 'Lorem ipsum dolor sit.', url: 'http://m.ly.com', desc: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat inventore minima voluptates.', img: 'http://placehold.it/150x150'});$('button').click(function () { // 1 ==> 朋友圈 2 ==> 敌人 0 ==> 间接弹出原生 mshare.init(+$(this).data('mshare'));});</script>上面是mshare.js的代码分享,把这些代码新建一个js文件放进去,而后在页面中引进就ok了。亨达返佣https://www.fx61.com/brokerli... /** * 此插件次要作用是在UC和QQ两个支流浏览器 * 下面触发微信分享到朋友圈或发送给敌人的性能 */'use strict';var UA = navigator.appVersion; /** * 是否是 UC 浏览器 */var uc = UA.split('UCBrowser/').length > 1 ? 1 : 0; /** * 判断 qq 浏览器 * 然而qq浏览器分高下版本 * 2 代表高版本 * 1 代表低版本 */var qq = UA.split('MQQBrowser/').length > 1 ? 2 : 0; /** * 是否是微信 */var wx = /micromessenger/i.test(UA); /** * 浏览器版本 */var qqVs = qq ? parseFloat(UA.split('MQQBrowser/')[1]) : 0;var ucVs = uc ? parseFloat(UA.split('UCBrowser/')[1]) : 0; /** * 获取操作系统信息 iPhone(1) Android(2) */var os = (function () { var ua = navigator.userAgent; if (/iphone|ipod/i.test(ua)) { return 1; } else if (/android/i.test(ua)) { return 2; } else { return 0; }}()); /** * qq浏览器上面 是否加载好了相应的api文件 */var qqBridgeLoaded = false; // 进一步细化版本和平台判断if ((qq && qqVs < 5.4 && os == 1) || (qq && qqVs < 5.3 && os == 1)) { qq = 0;} else { if (qq && qqVs < 5.4 && os == 2) { qq = 1; } else { if (uc && ((ucVs < 10.2 && os == 1) || (ucVs < 9.7 && os == 2))) { uc = 0; } }}/** * qq浏览器上面 依据不同版本 加载对应的bridge * @method loadqqApi * @param {Function} cb 回调函数 */function loadqqApi(cb) { // qq == 0 if (!qq) { return cb && cb(); } var script = document.createElement('script'); script.src = (+qq === 1) ? '//3gimg.qq.com/html5/js/qb.js' : '//jsapi.qq.com/get?api=app.share'; /** * 须要等加载过 qq 的 bridge 脚本之后 * 再去初始化分享组件 */ script.onload = function () { cb && cb(); }; document.body.appendChild(script);}/** * UC浏览器分享 * @method ucShare */function ucShare(config) { // ['title', 'content', 'url', 'platform', 'disablePlatform', 'source', 'htmlID'] // 对于platform // ios: kWeixin || kWeixinFriend; // android: WechatFriends || WechatTimeline // uc 分享会间接应用截图 var platform = ''; var shareInfo = null; // 指定了分享类型 if (config.type) { if (os == 2) { platform = config.type == 1 ? 'WechatTimeline' : 'WechatFriends'; } else if (os == 1) { platform = config.type == 1 ? 'kWeixinFriend' : 'kWeixin'; } } shareInfo = [config.title, config.desc, config.url, platform, '', '', '']; // android if (window.ucweb) { ucweb.startRequest && ucweb.startRequest('shell.page_share', shareInfo); return; } if (window.ucbrowser) { ucbrowser.web_share && ucbrowser.web_share.apply(null, shareInfo); return; }}/** * qq 浏览器分享函数 * @method qqShare */function qqShare(config) { var type = config.type; //微信好友 1, 微信朋友圈 8 type = type ? ((type == 1) ? 8 : 1) : ''; var share = function () { var shareInfo = { 'url': config.url, 'title': config.title, 'description': config.desc, 'img_url': config.img, 'img_title': config.title, 'to_app': type, 'cus_txt': '' }; if (window.browser) { browser.app && browser.app.share(shareInfo); } else if (window.qb) { qb.share && qb.share(shareInfo); } }; if (qqBridgeLoaded) { share(); } else { loadqqApi(share); }}/** * 对外裸露的接口函数 * @method mShare * @param {Object} config 配置对象 */function mShare(config) { this.config = config; this.init = function (type) { if (typeof type != 'undefined') this.config.type = type; try { if (uc) { ucShare(this.config); } else if (qq && !wx) { qqShare(this.config); } } catch (e) {} }}// 预加载 qq bridgeloadqqApi(function () { qqBridgeLoaded = true;});if (typeof module === 'object' && module.exports) { module.exports = mShare;} else { window.mShare = mShare;}

July 10, 2020 · 3 min · jiezi

h5中video标签的使用

video标签参数参数值形容x5-video-player-typeh5-page在x5内核浏览器中(安卓的qq、微信),视频不脱离文档流webkit-playsinlinetrueios 10中设置能够让视频在小窗内播放playsinlinetrue视频在小窗内播放preloadautononemetaauto时,主动进行预加载,none时,不进行预加载,meta时,只载入元数据autoplayautoplay则视频在就绪后马上播放。looploop循环播放事件事件形容play播放视频pause暂停视频play事件管制视频播放,返回一个promise对象,可用于判断视频是否能播放let video=this.$refs.video[0]video.load()video.play().then(res=>{ 视频播放胜利回调}).catch(function(err) { 视频播放失败回调 Toast('该视频暂不反对播放');});非凡场景1. ios零碎的微信中,非click状况下,video.play()生效场景: 须要页面滑动到指定地位时播放视频,但视频播放失败起因: 可能微信做了某些解决,必须click事件内,video.play()才有成果解决办法:暂无2. 低版本的安卓(安卓7)中,多个video标签场景: 低版本的安卓零碎中,点击多个视频播放后会导致一些视频首帧置灰展现起因: 可能是内存问题解决办法: 只应用一个video标签,每次要播放时再加载视频

July 8, 2020 · 1 min · jiezi

HTML5开发动态音频图

概要本次我们会使用html5和js开发一个动态音频图用到的技术点:(1)js(2)canvas + audio(3)Web Audio API 实现方式:(1)首先对于界面实现的考虑,由于区块非常多,我们使用传统dom节点实现是非常困难的(会占用大量的电脑CPU)。在这里,我们考虑使用canvas进行渲染(2)前端中,我们遵循尽量少用js控制dom节点的原则。能用css3实现的特效,就不要用js实现。(因为js不是标记语言,而是脚本语言,与html5不是同一种语言。会浪费浏览器大量时间加载,造成浏览器性能的浪费)。因此,用js就少用dom,用dom就尽量用css。(3)通过Web Audio API在音频节点上进行音频操作(即实现音频可视化),流程图如下: 在图中,音频上下文提供了音频处理的一套系统方法。输入源指音乐文件,通过名称引入;效果就是对输入源进行加工,如制作音频图、音波图、3D环绕、低音音效等;输出就是把效果输出到耳机、扬声器等目的地。 canvas引入在当下,除了布局使用dom节点外,特效基本都是通过canvas实现了。canvas好处:(1)写特效非常强大,性能优(2)用于做游戏。由于flash将于2020年退役,现在的游戏开始转向用html5制作(3)前端渲染大数据,数据可视化,大屏数据展示 canvas流程:通过js创建画笔<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style type="text/css"> *{ margin: 0; /* 外边距为0,使canvas能够占满全屏 */ } #canvas{ background: linear-gradient( 135deg, rgb(142,132,133) 0%, rgb(230,132,110) 100% ); /*创建线性渐变图像*/ } </style></head><body> <canvas id="canvas" width="500" height="500"></canvas> <script> var cxt=canvas.getContext('2d');//创建了画笔 cxt.beginPath();//开始画 cxt.closePath();//画完了 cxt.fillStyle='#f2f'; cxt.arc(250,250,100,0,2*Math.PI,false); cxt.fill(); </script></body></html>在创建线性渐变图像时,若100%后边加“,”,谷歌便加载不出来;若不加,便能加载出来。但是不知道为何这里尤其注意js里canvas的流程,创建画笔-》开始画-》画完了-》补充颜色、形状信息。其中前三步都是套路,只有如何去画根据任务的不同,代码不同Web Audio APi流程<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <audio id="audio" src="mp3/1.mp3" controls></audio> <script> var oCtx=new AudioContext();//创建音频对象 var oAudio=document.querySelector('audio'); var audioSrc=oCtx.createMediaElementSource(oAudio); //给音频对象创建媒体源 var analyser=oCtx.createAnalyser();//创建分析机 audioSrc.connect(analyser);//把分析机连接到媒体源上 analyser.connect(oCtx.destination);//把分析机得到的结果和扬声器相连 </script></body></html>这里要注意的是,audio中的autoplay、js中的audio.play()已经失效(谷歌浏览器的安全策略:声音不能自动播放,必须在用户有了操作后才能播放)上述流程中少了最关键的一步:如何分析音频数据,这一步根据实现的任务不同,内容不同。但是其余的步骤都是一样的,满满的套路动态音频图的开发<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style type="text/css"> *{ margin: 0; } #canvas{ background:linear-gradient( 135deg, rgb(142,132,133) 0%, rgb(230,132,110) 100% ); } </style></head><body> <audio id="audio" src="mp3/1.mp3" controls></audio> <button type="button" onclick="play()"></button> <canvas id="canvas"></canvas> <script> //先引入canvas画笔的创建流程 var cCxt=canvas.getContext('2d');//创建2D画笔 cCxt.beginPath();//开始画 cCxt.closePath();//画完了 //设计画布的样式 //设置画布大小为整个屏幕 canvas.width=window.innerWidth; canvas.height=window.innerHeight; //设置线条的渐变颜色 var oW=canvas.width; var oH=canvas.height; var color=cCxt.createLinearGradient(oW/2,oH/2,oW/2,oH/2-100); color.addColorStop(0,'#000'); color.addColorStop(.5,'#069'); color.addColorStop(1,'#f6f'); function play(){ //先引入API函数,走完Web Audio API的流程 var oCtx=new AudioContext();//创建音频对象 var oAudio=document.querySelector('audio'); var audioSrc=oCtx.createMediaElementSource(oAudio);//为音频对象创建媒体源 var analyser=oCtx.createAnalyser();//为音频对象创建分析机 audioSrc.connect(analyser);//将分析机与媒体源连接 analyser.connect(oCtx.destination);//将分析机与扬声器相连接 var count=80;//音频条的条数 var voiceHeight=new Uint8Array(analyser.frequencyBinCount);//建立一个数据缓冲区(此时为空) setInterval(draw(analyser,voiceHeight,count),1000); oAudio.play(); } function draw(analyser,voiceHeight,count){ analyser.getByteFrequencyData(voiceHeight);//将当前频率数据传入到无符号字节数组中,进行实时连接 var step=Math.round(voiceHeight.length/count);//每隔step个数,取一个数组里的数 for(var i=0;i<count;i++){ var audioHeight=voiceHeight[step*i]; cCxt.fillStyle=color; cCxt.fillRect(oW/2+(i*10),oH/2,7,-audioHeight); cCxt.fillRect(oW/2-(i*10),oH/2,7,-audioHeight); } //console.log(voiceHeight); } </script></body></html>上边的代码是不可行的,找了一下午也没找出错误到底出在哪里...问题主要如下:(1)在谷歌浏览器中,显示歌在播放,但是没有声音。console.log(voiceHeight)即图中注释部分没有注释掉时,voiceHeight只出现一次,而不是1000ms出现一次。没有图像 ...

July 1, 2020 · 1 min · jiezi

多图上传兼容微信小程序只能单图上传-开发uniapp项目遇到的问题主H5微信小程序

Demo:uni-app template GitHub Address问题需求中,需要上传多张图片,但是微信小程序只能单图上传,多图上传只支持App、H5。 如果只能一张一张上传图片的话,用户体验相当差,我相信上传的用户,会疯的…… 思路对此官方文档也给出了解决方案,就是重复调用api uni.uploadFile 解决方法我使用了uni-app插件市场中luck-request插件(仿axios封装request网络请求库,支持拦截器以及task操作),对于uni.uploadFile,也有封装。 在此,我在api.js的文件中又定义封装了上传图片的方法。 // 上传图片,接口名称根据自己后端定义,此接口调用不通export const uploadToOss = (params) => axios.upload('/api/upload/xxx', { ...params, custom: { loading: true, loadingTitle: '正在上传...' }})UploadPic.vue <template> <view class="upload_box"> <view class="image_list"> <view class="image_item" v-for="(item, index) in files" :key="index"> <view class="icon iconfont iconshanchu" @click="removeItem(index)"></view> <image :src="item"></image> </view> <view class="upload" v-if="files.length < limit" @click="handleUpload"> <view class="icon iconfont iconshangchuan"></view> <text>{{ btnText }}</text> </view> </view> <view class="upload_tip" v-if="tip">{{ tip }}</view> </view></template><script>import { uploadToOss } from '@/api/http';export default { props: { // 提示信息 tip: { type: String, default: '' }, // 按钮文字 btnText: { type: String, default: '' }, // 最多限制张数,默认1张 limit: { type: Number, default: 1 }, // 图片类型,用于同个页面多个图片上传组件区分 imgType: { type: String, default: 'image' } }, data() { return { files: [] // 上传图片后的图片地址集合 }; }, methods: { /** * @description 删除图片 * @param {Number} index 图片所在索引值 */ removeItem(index) { this.files.splice(index, 1); this.sendMessage(); // 发送信息父组件 }, /** * @description 上传图片 * @param {String} url 图片临时地址 */ uploadImg(url) { return new Promise((resolve, reject) => { uploadToOss({ filePath: url, fileType: 'image', name: 'file', }).then(res => { if(res._OK) { resolve(res.data.data); } else { uni.showModal({ title: '提示', content: res.data.message || '上传失败' }) return new Promise.reject(res); } }).catch(err => { return new Promise.reject(err); }) }) }, /** * @description 遍历调用upload接口 * @param {Array} tempFilePaths uni.chooseImage选择图片后返回的临时图片地址集合 * @returns {Array} arr 异步调用upload接口返回的图片服务器上的地址集合 */ async uploads(tempFilePaths) { let arr = []; for(let i = 0; i < tempFilePaths.length; i++) { arr[i] = await this.uploadImg(tempFilePaths[i]); } return arr; }, /** * @description 上传图片 */ handleUpload() { let _this = this; uni.chooseImage({ count: _this.limit, async success(chooseImageRes) { // 选择图片后,立即调用图片上传的接口,es6的await必须被async包含。 let files = await _this.uploads(chooseImageRes.tempFilePaths); _this.files = [..._this.files, ...files]; _this.sendMessage(); // 发送信息父组件 } }); }, /** * @description 发送信息给父组件 */ sendMessage() { this.$emit('on-change', { files: this.files, imgType: this.imgType }); } }};</script><style lang="scss" scoped>.upload_box { .upload { width: 120rpx; height: 120rpx; border: 2rpx solid #d9d9d9; font-size: 24rpx; color: #999999; display: flex; justify-content: center; flex-direction: column; align-items: center; margin-bottom: 20rpx; .iconfont { margin-bottom: 8rpx; } } .image_list { display: flex; flex-wrap: wrap; .image_item { position: relative; width: 120rpx; height: 120rpx; margin: 0 20rpx 20rpx 0; z-index: 1; image { width: 120rpx; height: 120rpx; } .iconfont { color: #ff0000; font-size: 30rpx; position: absolute; z-index: 10; right: 0; top: 0; } } }}.upload_tip { color: #999999; font-size: 24rpx; margin-top: -10rpx;}</style>总结该多图上传组件的交互跟拓展性还不够好,但基本满足了目前产品的需求,之后还是会持续迭代更新的,也希望大家多多提意见???????????? ...

June 29, 2020 · 2 min · jiezi

HTML5CSS3知识总结

愿你被整个世界温柔以待 新增语义化标签header:头部标签nav:导航标签article:文章内容标签section:块级标签aside:侧边栏标签footer:底部标签注意: 语义化标签主要针对于搜索引擎的可以在界面使用多次的,主要适用于移动端开发在IE9中,需要把这些元素转换为块级元素(很重要)新增input表单 ????这个新增的表单属性,在实际开发中是运用的比较广的,需要掌握: 代码示例: CSS3新增属性伪类(伪类选择器)伪类:同一个标签,根据其不同的种状态,有不同的样式。这就叫做“伪类”。伪类用冒号来表示。 比如div是属于box类,这一点很明确,就是属于box类。但是a属于什么类?不明确。因为需要看用户点击前是什么状态,点击后是什么状态。所以,就叫做“伪类”。 静态伪类和动态伪类伪类选择器分为两种。 (1)静态伪类:只能用于超链接的样式。如下: :link 超链接点击之前:visited 链接被访问过之后PS:以上两种样式,只能用于超链接。 (2)动态伪类:针对所有标签都适用的样式。如下: :hover “悬停”:鼠标放到标签上的时候:active “激活”: 鼠标点击标签,但是不松手时。:focus 是某个标签获得焦点时的样式(比如某个输入框获得焦点)PS:以上三种样式,只能用于超链接。 超链接a标签超链接的四种状态a标签有4种伪类(即对应四种状态),要求背诵。如下: :link “链接”:超链接点击之前:visited “访问过的”:链接被访问过之后:hover “悬停”:鼠标放到标签上的时候:active “激活”: 鼠标点击标签,但是不松手时。对应的代码如下:(不带注释) a:link{ color:red; } a:visited{ color:orange; } a:hover{ color:green; } a:active{ color:black; }对应的代码如下:(带注释) /*让超链接点击之前是红色*/ a:link{ color:red; } /*让超链接点击之后是绿色*/ a:visited{ color:orange; } /*鼠标悬停,放到标签上的时候*/ a:hover{ color:green; } /*鼠标点击链接,但是不松手的时候*/ a:active{ color:black;记住,在css中,这四种状态必须按照固定的顺序写: a:link 、a:visited 、a:hover 、a:active如果不按照顺序,那么将失效。“爱恨准则”:love hate。必须先爱,后恨。 看一下这四种状态的动图效果: 超链接的美化问:既然a{}定义了超链的属性,和a:link{}定义了超链点击之前的属性,那这两个有啥区别呢? 答: a{}和a:link{}的区别: a{}定义的样式针对所有的超链接(包括锚点)a:link{}定义的样式针对所有写了href属性的超链接(不包括锚点)超链接a标签在使用的时候,比较难。因为不仅仅要控制a这个盒子,也要控制它的伪类。 ...

June 28, 2020 · 2 min · jiezi

学习笔记HTML入门基础一

1.文本格式合并单元格colspan(横向合并)rowspan(纵向合并)input元素的多种形态,如 文本 密码 单选 按钮等type属性来定义<form action="url地址" method="get/post" name="表单名称"><p>文本</p>用户名:<input type="text" name="username">密码:<input type="password" name="password">单选:<input tyype="radio" name="sex" value="nan">男<input tyype="radio" name="sex" value="nv">女多选:<input type="checkbox" name="n">吃饭<input type="checkbox" name="n">睡觉重置:<input type="reset" name="">提交:<input type="submit" name=""> input的属性:只读:readonly禁用:disabled默认选:checked提示文本:placehoder输入光标显示:autofocus 2.映入、文件引入、框架<select size="2"> <option>苹果</option><option>西瓜</option><option>南瓜</option></select> size:出现下拉框 disable:不可以选 multiple:多选 selected:默认选<textarea rows="4" cols="5" maxlength="5">rows行数 cols:输入可见宽度 maxlength:输入最大字符数 网页<a href="http://www.baidu.com"> 链接</a> 文档

June 27, 2020 · 1 min · jiezi

如何理解HTML5语义化的

以前的html结构,就是 DIV + CSS,代码很难理解,为了改变这种这种状况,提出了让HTML语义化。 例如:段落用 p 标签,标题用 h 系列标签。 优点: 更省代码:更少的HTML代码。 更清晰的DOM结构,有利于书写css和js;更有利于SEO:可以提高搜索引擎的有效爬取,提高网站的流量;可读性:如果不幸网站的CSS样式丢失,清晰的结构依然使你的页面可读性较高;互用性:语义化的HTML代码,让你的团队维护更轻松。

June 16, 2020 · 1 min · jiezi

2020最新微信域名防封技术打破谣言

<!-- /\* Font Definitions \*/ @font-face {font-family:宋体; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-alt:SimSun; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 680460288 22 0 262145 0;} @font-face {font-family:"Cambria Math"; panose-1:2 4 5 3 5 4 6 3 2 4; mso-font-charset:1; mso-generic-font-family:roman; mso-font-format:other; mso-font-pitch:variable; mso-font-signature:0 0 0 0 0 0;} @font-face {font-family:微软雅黑; panose-1:2 11 5 3 2 2 4 2 2 4; mso-font-charset:134; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-2147483001 672087122 22 0 262175 0;} @font-face {font-family:Tahoma; panose-1:2 11 6 4 3 5 4 4 2 4; mso-font-charset:0; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-520081665 -1073717157 41 0 66047 0;} @font-face {font-family:Consolas; panose-1:2 11 6 9 2 2 4 3 2 4; mso-font-charset:0; mso-generic-font-family:modern; mso-font-pitch:fixed; mso-font-signature:-520092929 1073806591 9 0 415 0;} @font-face {font-family:"\\@微软雅黑"; panose-1:2 11 5 3 2 2 4 2 2 4; mso-font-charset:134; mso-generic-font-family:swiss; mso-font-pitch:variable; mso-font-signature:-2147483001 672087122 22 0 262175 0;} @font-face {font-family:"\\@宋体"; panose-1:2 1 6 0 3 1 1 1 1 1; mso-font-charset:134; mso-generic-font-family:auto; mso-font-pitch:variable; mso-font-signature:3 680460288 22 0 262145 0;} /\* Style Definitions \*/ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-unhide:no; mso-style-qformat:yes; mso-style-parent:""; margin-top:0cm; margin-right:0cm; margin-bottom:10.0pt; margin-left:0cm; mso-pagination:widow-orphan; layout-grid-mode:char; mso-layout-grid-align:none; font-size:11.0pt; font-family:"Tahoma","sans-serif"; mso-fareast-font-family:微软雅黑; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi;} span.hljs-number {mso-style-name:hljs-number; mso-style-unhide:no;} .MsoChpDefault {mso-style-type:export-only; mso-default-props:yes; font-size:11.0pt; mso-ansi-font-size:11.0pt; mso-fareast-font-family:微软雅黑; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:0pt;} .MsoPapDefault {mso-style-type:export-only; margin-bottom:10.0pt; line-height:11.0pt;} /\* Page Definitions \*/ @page {mso-page-border-surround-header:no; mso-page-border-surround-footer:no;} @page WordSection1 {size:595.3pt 841.9pt; margin:72.0pt 90.0pt 72.0pt 90.0pt; mso-header-margin:35.4pt; mso-footer-margin:35.4pt; mso-paper-source:0;} div.WordSection1 {page:WordSection1;} /\* List Definitions \*/ @list l0 {mso-list-id:279725072; mso-list-template-ids:-1797495592;} ol {margin-bottom:0cm;} ul {margin-bottom:0cm;} --> 2020最新微信域名防封技术打破谣言 ...

June 10, 2020 · 2 min · jiezi

Egret-536-正式发布大幅改进编译速度

各位开发者大家好, 我们今天正式发布 Egret 5.3.6 版本。Egret 5.3.6 仍然是一个抢先预览版,在这个版本中我们为各位开发者带来如下功能: JavaScript模块支持全新的 EuiCompiler运行时改进Inspector更新其中 JavaScript模块支持和EuiCompiler我们建议正在开发中尚未上线的项目使用,运行时以及 Inspector 更新所有项目均可使用。 JavaScript 模块支持,大幅改进编译速度(beta)这项技术我们率先使用在 EgretPro中,在这个版本我们将其移植到了 Egret 5.3 版本中,以保证尽可能多的存量开发者可以使用此功能。 在不支持模块的老式浏览器中,如果一个 JavaScript 文件要想给另外一个文件暴露出去一部分数据或者一个变量,那只能将它定义在全局的作用域下,并根据他们的依赖关系来决定加载顺序。 白鹭引擎构建管线中的 IncrementBuild 插件将会分析您的游戏逻辑依赖关系,并生成一个 manifest.json 文件,游戏在调试时会加载这个 manifest.json ,根据其中的脚本顺序逐个加载 JavaScript 代码。 这种方式带来的问题是,随着项目不断变大,IncrementBuild 插件的依赖关系检查会使游戏的编译速度会越来越慢,而 JavaScript 模块中,由于所有的依赖关系都是显式设置的,所以不存在此问题。 在 5.3.6 版本中,我们允许开发者使用 JavaScript模块,并提供了 webpack 打包器,它会将多个 JavaScript 模块文件打包为一个文件,使得其可以在不支持JavaScript模块的旧式浏览器上运行。 经过与白鹭合作的开发者抢先测试,将一个代码总量20万行,包含1700个 TypeScript 文件的重度挂机游戏项目迁移至 JavaScript Module 格式花费了一人一个星期时间,而这份投入非常的值得。经过开发者的实际反馈,在修改前编译速度大约为40秒一次,修改后仅需3-4秒,这可以为每位前端开发人员每天节省一小时的时间。 如果您对这项改动感兴趣,但是担心项目过大而感觉无从下手,可以联系白鹭引擎的官方技术支持(微信号:egretengine),寻求白鹭引擎官方团队的支持。 全新的 EuiCompiler (beta)在使用白鹭引擎开发的游戏中,UI以及相关逻辑是工作量最大的部分之一。在 Egret 5.3 系列版本中,我们决定全面改进 UI的 开发体验。为此我们先是发布了全新的 Egret UI Editor,并逐步迭代至1.9版本。除此之外,我们将在这个版本中为大家带来全新的 EuiCompiler。 EuiCompiler 是引擎中的 ExmlPlugin 的升级版本,他实现了和 ExmlPlugin 一致的功能,但是内部代码结构更加清晰并具备可扩展性。开发者可以扩展 EuiCompiler,为其加入符合自己游戏需求的定制功能,诸如:浮点数值擦除,国际化语言包、资源文件检查等。 ...

June 4, 2020 · 1 min · jiezi

庆祝DCloud手机引擎月活突破10亿

成绩,来自于开发者的支持和信任。 成绩,只属于历史。 未来,我们将继续认真、扎实的做好每件事,为开发者提供更好的产品和服务!

June 4, 2020 · 1 min · jiezi

CSS样式书写顺序和命名规范

书写顺序的意义减少浏览器reflow(回流),提升浏览器渲染dom的性能①:解析html构建dom树,解析css构建css树:将html解析成树形的数据结构,将css解析成树形的数据结构 ②:构建render树:DOM树和CSS树合并之后形成的render树。 ③:布局render树:有了render树,浏览器已经知道那些网页中有哪些节点,各个节点的css定义和以及它们的从属关系,从而计算出每个节点在屏幕中的位置。 ④:绘制render树:按照计算出来的规则,通过显卡把内容画在屏幕上。 css样式解析到显示至浏览器屏幕上就发生在②③④步骤,可见浏览器并不是一获取到css样式就立马开始解析而是根据css样式的书写顺序将之按照dom树的结构分布render样式,完成第②步,然后开始遍历每个树结点的css样式进行解析,此时的css样式的遍历顺序完全是按照之前的书写顺序。 优先级第一--定位属性: { display 规定元素应该生成的框的类型。 position 定位规定元素的定位类型。 float 规定框是否应该浮动。 left top right bottom overflow 规定当内容溢出元素框时发生的事情。 clear 清除 z-index 设置元素的堆叠顺序。 content 内容 list-style visibility 可见性/显示 } 优先级第二--自身属性: { width height border padding margin background} 优先级第三--文字样式: { font-family font-size font-style 规定文本的字体样式。 font-weight font-varient 规定是否以小型大写字母的字体显示文本 color } 优先级第四--文本属性: { text-align 规定文本的水平对齐方式。 vertical-align 设置元素的垂直对齐方式。 text-wrap 规定文本的换行规则。 text-transform 控制文本的大小写。 text-indent 规定文本块首行的缩进。 text-decoration 规定添加到文本的装饰效果。 letter-spacing 设置字符间距。 word-spacing 设置单词间距。 white-space 规定如何处理元素中的空白。 text-overflow 规定当文本溢出包含元素时发生的事情。 } 优先级第五--CC3中新增属性: { box-shadow 向方框添加一个或多个阴影。 cursor 规定要显示的光标的类型(形状)。 border-radius background:linear-gradient transform…… 向元素应用 2D 或 3D 转换。 }CSS代码的命名规范必须以字母开头命名选择器,这样可保证在所有浏览器下都能兼容;不允许单个字母的类选择器出现;不允许命名带有广告等英文的单词,例如ad,adv,adver,advertising,已防止该模块被浏览器当成垃圾广告过滤掉。任何文件的命名均如此。下划线 ’ _ ’ 禁止出现在class命名中,全小写,统一使用’-‘连字符.禁止驼峰命名 className见名知意CSS代码注意事项不要以完全没有语义的标签作为选择器,这会造成大面积污染简写CSS颜色属性值删除CSS属性值为0的单位删除无用CSS样式为单个CSS选择器或新申明开启新行避免过度简写 , .ico足够表示这是一个图标 , 而.i不代表任何意思使用有意义的名称,使用结构化或者作用目标相关的,而不是抽象的名称

June 3, 2020 · 1 min · jiezi

应用connectedreactrouter和reduxthunk打通react路由孤立

redux在我们开发过程中,很多时候,我们需要让组件共享某些数据,虽然可以通过组件传递数据实现数据共享,但是如果组件之间不是父子关系的话,数据传递是非常麻烦的,而且容易让代码的可读性降低,这时候我们就需要一个 state(状态)管理工具。常见的状态管理工具有 redux,mobx,这里选择 redux 进行状态管理。值得注意的是 React 16.3 带来了全新的Context API,我们也可以使用新的 Context API 做状态管理。Redux 是负责组织 state 的工具,但你也要考虑它是否适合你的情况。 在下面的场景中,引入 Redux 是比较明智的: 你有着相当大量的、随时间变化的数据你的 state 需要有一个单一可靠数据来源你觉得把所有 state 放在最顶层组件中已经无法满足需要了的确,这些场景很主观笼统。因为对于何时应该引入 Redux 这个问题,对于每个使用者和每个应用来说都是不同的。 对于 Redux 应该如何、何时使用的更多建议,请看: “您可能不需要Redux”Redux之道,第1部分-实现和意图Redux之道,第2部分-实践与哲学Redux 常见问题Redux 的创造者 Dan Abramov 又补充了一句 "只有遇到 React 实在解决不了的问题,你才需要 Redux 。" react-reduxreact-redux 提供Provider组件通过 context 的方式向应用注入 store,然后组件使用connect高阶方法获取并监听 store,然后根据 store state 和组件自身的 props 计算得到新的 props,注入该组件,并且可以通过监听 store,比较计算出的新 props 判断是否需要更新组件。 render( <Provider store={store}> <ConnectedRouter history={history}> <App /> </ConnectedRouter> </Provider>, document.getElementById('app'))复制代码整合 redux 到 react 应用合并 reducer在一个 react 应用中只有一个 store,组件通过调用 action 函数,传递数据到 reducer,reducer 根据数据更改对应的 state。但是随着应用复杂度的提升,reducer 也会变得越来越大,此时可以考虑将 reducer 拆分成多个单独的函数,拆分后的每个函数负责独立管理 state 的一部分。 ...

May 31, 2020 · 4 min · jiezi

canvas实现稍复杂的财务报表

<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <style> /*#oCan{ width: 1000px; height: 650px; }*/ </style> </head> <body style="background-color:#fff"> <canvas id="oCan" width="1600" height="650"> ~~~~</canvas> <script> var canvas = document.getElementById('oCan'); let dateArr = []; let years = 2016; let month = 0; var startX = 100; var startY = 600; var ctx = canvas.getContext('2d'); const salary = ['0','50k','100k','150k']; const data = [89815.85,65441.37,68983.43,80520.7,71360.8,71308.09,97843.1,74963.94,72430.98,81891.22,126140.22,57712.01,85631.15, 68464.57,73150.3,91573.05,81404.2,76903.97,71145.33,81884.51,90759.47,80811.97,76918.84,99999.56]; for(let i = 0 ; i<24;i++){ ++month; month = transFromMonthType(month); dateArr.push(years + '-' + month); if(month === 12){ years++; month = 0; } } function transFromMonthType (date) { if(date < 10){ return "0" + date + '' } return date } console.log(dateArr); //建立坐标系 function create(){ ctx.beginPath(); //轴线 ctx.moveTo(startX,startY); ctx.lineTo(startX,0); ctx.moveTo(startX,startY); ctx.lineTo(1600,startY); ctx.strokeStyle="#ccc"; ctx.stroke(); ctx.closePath(); ctx.beginPath(); //横线 ctx.moveTo(startX,400); ctx.lineTo(1600,400); ctx.moveTo(startX,200); ctx.lineTo(1600,200); ctx.moveTo(startX,0); ctx.lineTo(1600,0); ctx.strokeStyle="#ccc"; ctx.stroke(); ctx.closePath(); } //填充横纵坐标 function insert(){ var x = 100; var y = 600; var offsetY = 600 * (1-(data[0]/150000)); //绘制横坐标 ctx.moveTo(125,offsetY); ctx.fillText(data[0],110,offsetY-5); for(var i= 0; i <dateArr.length;i++){ ctx.textAlign="start"; ctx.fillText(dateArr[i],x,y+20); x += 50; if(i > 0){ offsetY = 600 * (1-(data[i]/150000)); ctx.textAlign="right"; ctx.fillText(data[i],x,offsetY-5); buildLineChart(x,offsetY); } } x = 100; y = 600; //绘制纵坐标 for(let j = 0 ;j < salary.length;j++){ ctx.textAlign = 'center'; ctx.fillText(salary[j],x-40,y); y -= 197;//偏差问题,如果设置200那么salary[3]的文本‘15k’就会被隐藏~~~~ } } //建立折线图 function buildLineChart(x,y){ let yet1 = y; ctx.lineTo(x-25,yet1); ctx.strokeStyle = "#3FA7DC"; ctx.stroke(); } insert(); create(); </script> </body></html>整体思路:1.设计y轴价格和x轴日期区间。注意:此demo的价格区间只限于0<=price<150000;超出的部分会被省略2.日期区间主要通过for循环将字符串日期推送到特定数组中,并调用特定函数来实现个位数加0操作。3.create方法主要用来创建x和y轴,以及特定数量的横线。利用ctx.beginPath();ctx.closePath();~~~~实现开辟以及结束一个工作区,在工作区给线条改颜色。4.调用insert()来设置和填充横纵坐标,以及通过moveTo()开始一条路径buildLineChart()方法用来绘制折线图,具体通过lineTo方法连接这些线,然后就呈现成折线。5.此文章难点还是在于lineTo的y轴坐标问题,要让他和金额以及距离canvas对象原点y轴比例的问题。即纵坐标offsetY = 600 * (1-(data[i]/150000)); ...

May 28, 2020 · 1 min · jiezi

CSS魔法堂:改变单选框颜色就这么吹毛求疵!

前言 是否曾经被业务提出"能改改这个单选框的颜色吧!让它和主题颜色搭配一下吧!",然后苦于原生不支持换颜色,最后被迫自己手撸一个凑合使用。若抛开input[type=radio]重新开发一个,发现要模拟选中、未选中、不可用等状态很繁琐,而涉及单选框组就更烦人了,其实我们可以通过label、::before、:checked和tabindex,然后外加少量JavaScript脚本就能很好地模拟出一个样式更丰富的“原生”单选框。下面我们一起来尝试吧!单选框了解一下 由于我们的目标是改变单选框颜色,其他外观特征和行为与原来的单选框一致,那么我们就必须先了解单选框原来的外观特征和行为主要有哪些。1.外观特征1.1.常态样式margin: 3px 3px 0px 5px;border: none 0;padding: 0;box-sizing: border-box;display: inline-block;line-height: normal;position: static;注意:外观上我们必须要保证布局特性和原生的一致,否则采用自定义单选框替换后很大机会会影响整体的布局,最后导致被迫调整其他元素的布局特性来达到整体的协调,从而扩大了修改范围。1.2.获得焦点的样式outline-offset: 0px;outline: -webkit-focu-ring-color auto 5px;注意:这里的获取焦点的样式仅通过键盘Tab键才生效,若通过鼠标点击虽然单选框已获得焦点,但上述样式并不会生效。1.3.设置为disabled的样式color: rgb(84, 84, 84);2.行为特征 单选框的行为特征,明显就是选中与否,及选中状态的改变事件,因此我们必须保持对外提供change事件。 另外值得注意的是,当通过键盘的Tab键让单选框获得焦点后,再按下Space键则会选中该单选框。 有了上述的了解,我们可以开始着手撸代码了!少废话,撸代码上图中左侧就是原生单选框,右侧为我们自定义的单选框。从上到下依次为未选中、选中、获得焦点和disabled状态的样式。CSS部分label.radio { /* 保证布局特性保持一致 / margin: 3px 3px 0px 5px; display: inline-block; box-sizing: border-box; width: 12px; height: 12px;}.radio__appearance{ display: block; / 设置为block则不受vertical-align影响,从而不会意外影响到.radio的linebox高度 / position: relative; box-shadow: 0 0 0 1px tomato; / box-shadow不像border那样会影响盒子的框高 / border-radius: 50%; height: 90%; width: 90%; text-align: center;}label.radio [type=radio] + .radio__appearance::before{ content: “”; display: block; border-radius: 50%; width: 85%; height: 85%; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); transition: background .3s;}label.radio [type=radio]:checked + .radio__appearance::before{ background: tomato;}label.radio [type=radio][disabled] + .radio__appearance{ opacity: .5;}label.radio:focus{ outline-offset: 0px; outline: #999 auto 5px;}/ 通过鼠标单击获得焦点时,outline效果不生效 /label.radio.clicked{ outline: none 0;}/ 自定义单选框的行为主要是基于原生单选框的,因此先将原生单选框隐藏 */label.radio input { display: none;}HTML部分<!– 未选中状态 –><label class=“radio” tabindex=“0”> <input type=“radio” name=“a”> <i class=“radio__appearance”></i></label><br><!– 选中状态 –><label class=“radio” tabindex=“0”> <input type=“radio” name=“a” checked> <i class=“radio__appearance”></i></label><br><!– disabled状态 –><label class=“radio”> <input type=“radio” name=“a” disabled> <i class=“radio__appearance”></i></label>JavaScript部分var radios = document.querySelectorAll(".radio")radios.forEach(radio => { // 模拟鼠标点击后:focus样式无效 radio.addEventListener(“mousedown”, e => { var tar = e.currentTarget tar.classList.add(“clicked”) var fp = setInterval(function(){ if (document.activeElement != tar){ tar.classList.remove(“clicked”) clearInterval(fp) } }, 400) }) // 模拟通过键盘获得焦点后,按Space键执行选中操作 radio.addEventListener(“keydown”, e => { if (e.keyCode === 32){ e.target.click() } })})这个实现有3个注意点:通过label传递鼠标点击事件到关联的input[type=radio],因此可以安心隐藏单选框又可以利用单选框自身特性。但由于label控件自身的限制,如默认不是可获得焦点元素,因此无法传递键盘按键事件到单选框,即使添加tabindex特性也需手写JS来实现;当tabindex大于等于0时表示该元素可以获得焦点,为0时表示根据元素所在位置安排获得焦点的顺序,而大于0则表示越小越先获得焦点;由于单选框的display为inline-block,因此单选框将影响line box高度。当自定义单选框内元素采用inline-block时,若vertical-align设置稍有不慎就会导致内部元素所在的line box被撑高,从而导致自定义单选框所在的line box高度变大。因此这里采用将内部元素的display均设置为block的做法,直接让vertical-align失效,提高可控性。总结 对于复选框我们可以稍加修改就可以了,然后通过VUE、React等框架稍微封装一下提供更简约的API,使用起来就更方便了。 尊重原创,转载请注明来自:https://www.cnblogs.com/fsjoh… ^_^肥仔John ...

October 4, 2018 · 1 min · jiezi

用canvas画心电图

效果图:思路:1.模拟点(如果你有真实的数据,那就是把数据幻化成canvas对应的坐标点) 模拟点时注意的点就是高起部分需要对称以及为了好看要随机出现上上下下2.画线 画线需要注意有一个匀速移动的过程。 比如 A点到B点,不是简单的A画到B,而是A点到A1,A2….最后到B(这一块按照比例移动比较难)3.画线的一些效果,比如加上阴影(这里就可以自由发挥了)具体代码<!DOCTYPE html> <html lang=“en”> <head> <meta charset=“UTF-8”> <title>心电图</title> <meta name=“viewport” content=“width=device-width, initial-scale=1, user-scalable=no”> <style> html,body{ width: 100%; height: 100%; margin: 0; } canvas{ background: #000; width: 100%; height: 100%; } </style> </head> <body> <div id=“canvas”> <canvas id=“can”></canvas> </div> <script> var can = document.getElementById(‘can’), pan, index = 0, flag = true, wid = document.body.clientWidth, hei = document.body.clientHeight, x = 0, y = hei/2, drawX = 0, drawY = hei/2, drawXY = [], cDrawX = 0, i = 0, reX = 0, reY = 0; start(); function start(){ can.height = hei; can.width = wid; pan = can.getContext(“2d”); pan.strokeStyle = “white”; pan.lineJoin = “round”; pan.lineWidth = 6; pan.shadowColor = “#228DFF”; pan.shadowOffsetX = 0; pan.shadowOffsetY = 0; pan.shadowBlur = 20; pan.beginPath(); pan.moveTo(x,y); drawXYS(); index = setInterval(move,1); }; function drawXYS(){ if(drawX > wid){ }else{ if(drawY == hei/2){ if(flag){ flag = false; }else{ var _y = Math.ceil(Math.random()*10); _y = _y/2; if(Number.isInteger(_y)){ drawY += Math.random()*180+30; }else{ drawY -= Math.random()*180+30; } flag = true; } cDrawX = Math.random()*40+15; }else{ drawY = hei/2; } drawX += cDrawX; drawXY.push({ x : drawX, y : drawY }); drawXYS(); } } function move(){ var x = drawXY[i].x, y = drawXY[i].y; if(reX >= x - 1){ reX = x; reY = y; i++; cc(); return; } if(y > hei/2){ if(reY >= y){ reX = x; reY = y; i++; cc(); return; } }else if(y < hei/2){ if(reY <= y){ reX = x; reY = y; i++; cc(); return; } }else{ reX = x; reY = y; i++; cc(); return; } reX += 1; if(y == hei/2){ reY = hei/2; }else{ var c = Math.abs((drawXY[i].x-drawXY[i-1].x)/(drawXY[i].y-drawXY[i-1].y)); var _yt = (reX-drawXY[i-1].x)/c; if(drawXY[i].y < drawXY[i-1].y){ reY = drawXY[i-1].y - _yt; }else{ reY = drawXY[i-1].y + _yt; } } cc(); } function cc(){ if(i == drawXY.length){ pan.closePath(); clearInterval(index); index = 0; x = 0; y = hei/2; flag = true; i = 0; }else{ pan.lineTo(reX, reY); pan.stroke(); } } </script></body></html>备注代码没有注释,如果有看不懂的地方,可以联系我sf上联系不到的话可以联系我的公众号:乐趣区 ...

September 7, 2018 · 2 min · jiezi

「造个轮子」——cicada 源码分析

前言两天前写了文章[《「造个轮子」——cicada(轻量级 WEB 框架)》](https://crossoverjie.top/2018… 向大家介绍了 cicada 之后收到很多反馈,也有许多不错的建议。同时在 GitHub 也收获了 80 几颗 小♥♥(绝对不是刷的。。)也有朋友希望能出一个源码介绍,本文就目前的 v1.0.1 版本来一起分析分析。没有看错,刚发布就修复了一个 bug,想要试用的请升级到 1.0.1 吧。技术选型一般在做一个新玩意之前都会有技术选型的过程,但这点在做 cicada 的时候却异常简单。因为我的需求是想提供一个高性能的 HTTP 服务,纵观整个开源界其实选择不多。加上最近我在做 Netty 相关的开发,所以自然而然就选择了它。同时 Netty 自带了对 HTTP 协议的编解码器,可以非常简单快速的开发一个 HTTP 服务器。我只需要把精力放在参数处理、路由等业务处理上即可。同时 Netty 也是基于 NIO 实现,性能上也有保证。关于 Netty 相关内容可以参考这里。下面来重点分析其中的各个过程。路由规则最核心的自然就是 HTTP 的处理 handle,对应的就是 HttpHandle 类。查看源码其实很容易看出具体的步骤,注释也很明显。这里只分析重点功能。先来考虑下需求。首先作为一个 HTTP 框架,自然是得让使用者能有地方来实现业务代码;就像咱们现在使用 SpringMVC 时写的 controller 一样。其实当时考虑过三种方案:像 SpringMVC 一样定义注解,只要声明了对应注解我就认为这是一个业务类。用过 Struts2 的同学应该有印象,它的业务类 Action 都是配置到一个 XML 中;在里面配置接口对应的业务处理类。同样的思路,只是把 XML 文件换成 properties 配置文件,在里面编写 JSON 格式的对应关系。这时就得分析各个方案的优缺点了。方案二和三其实就是 XML 和 json 的对比了;XML 会让维护者感到结构清晰,同时便于维护和新增。JSON 就不太方便处理了,并且在这样的场景并不用于传输自然也发挥不出优势。最后考虑到现在流行的 SpringBoot 都在去 XML,要是再搞一个依赖于 XML 的东西也跟不上大家的使用习惯。于是就采用类似于 SpringMVC 这样的注解形式。既然采用了注解,那框架怎么知道用户访问某个接口时能对应到业务类呢?所以首先第一步自然是需要将加有注解的类全部扫描一遍,放到一个本地缓存中。这样才能方便后续的路由定位。路由策略其中核心的源码在 routeAction 方法中。首先会全局扫描使用了 @CicadaAction 的注解,然后再根据请求地址找到对应的业务类。全局扫描代码:首先是获取到项目中自定义的所有类,然后判断是否加有 @CicadaAction 注解。是目标类则把他缓存到一个本地 Map 中,方便下次访问时可以不再扫描直接从缓存中获取即可(反射很耗性能)。执行完 routeAction 后会获得真正的业务类类型。Class<?> actionClazz = routeAction(queryStringDecoder, appConfig);传参方式拿到业务类的类类型之后就成功一大半了,只需要反射生成它的对象然后执行方法即可。在执行方法之前又要涉及到一个问题,参数我该怎么传递呢?考虑到灵活性我采用了最简答 Map 方式。因此定义了一个通用的 Param 接口并继承了 Map 接口。public interface Param extends Map<String, Object> { /** * get String * @param param * @return / String getString(String param); /* * get Integer * @param param * @return / Integer getInteger(String param); /* * get Long * @param param * @return / Long getLong(String param); /* * get Double * @param param * @return / Double getDouble(String param); /* * get Float * @param param * @return / Float getFloat(String param); /* * get Boolean * @param param * @return */ Boolean getBoolean(String param) ;}其中封装了几种基本类型的获取方式。同时在 buildParamMap() 方法中,将接口中的参数封装到这个 Map 中。Param paramMap = buildParamMap(queryStringDecoder);业务执行最后只需要执行业务即可;由于在上文已经获取到业务类的类类型,所以这里通过反射即可调用。同时也定义了一个业务类需要实现的一个通用接口 WorkAction,想要实现具体业务只要实现它就行。而这里的方法参数自然就是刚才定义的参数接口 Param。由于所有的业务类都是实现了 WorkAction,所以在反射时都可以定义为 WorkAction 对象。WorkAction action = (WorkAction) actionClazz.newInstance();WorkRes execute = action.execute(paramMap);最后将构建好的参数 map 传入即可。响应返回有了请求那自然也得有响应,观察刚才定义的 WorkAction 接口可以发现其实定义了一个 WorkRes 响应类。所有的响应数据都需要封装到这个对象中。这个没啥好说的,都是一些基本数据。最后在 responseMsg() 方法中将响应数据编码为 JSON 输出即可。拦截器设计拦截器也是一个框架基本的功能,用处非常多。cicada 的实现原理非常简单,就是在 WorkAction 接口执行业务逻辑之前调用一个方法、执行完毕之后调用另一个方法。也是同样的思路需要定义一个接口 CicadaInterceptor,其中有两个方法。看方法名字自然也能看出具体作用。同时在这两个方法中执行具体的调用。这里重点要看看 interceptorBefore 方法。其中也是加入了一个缓存,尽量的减少反射操作。适配器就这样的拦截器接口是够用了,但并不是所有的业务都需要实现两个接口。因此也提供了一个适配器 AbstractCicadaInterceptorAdapter。它作为一个抽象类实现了 CicadaInterceptor 接口,这样后续的拦截业务也可继承该接口选择性的实现方法即可。类似于这样:总结v1.0.1 版本的 cicada 就介绍完毕了,其中的原理和源码都比较简单。大量使用了反射和一些设计模式、多态等应用,这方面经验较少的朋友可以参考下。同时也有很多不足;比如传参后续会考虑更加优雅的方式、拦截器目前写的比较死,后续会利用动态代理实现自定义拦截。其实 cicada 只是利用周末两天时间做的,bug 肯定少不了;也欢迎大家在 GitHub 上提 issue 参与。最后贴下项目地址:https://github.com/TogetherOS/cicada你的点赞与转发是最大的支持。 ...

September 5, 2018 · 2 min · jiezi

前端每日实战:127# 视频演示如何用纯 CSS 创作一个圆环旋转错觉动画(实际上没有任何元素在做旋转运动)

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/oPWJNj/可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。https://scrimba.com/p/pEgDAM/cbvPWHM源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读定义 dom,容器中包含 10 个 <div> 子元素,每个 <div> 子元素内还有一个 <span> 子元素:<figure class=“container”> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div> <div><span></span></div></figure>定义容器尺寸:.container { width: 17em; height: 17em; font-size: 16px;}定义子元素的尺寸,和容器相同:.container { position: relative;}.container div { position: absolute; width: inherit; height: inherit;}在子元素的正中画一个黄色的小方块:.container div { display: flex; align-items: center; justify-content: center;}.container span { position: absolute; width: 1em; height: 1em; background-color: yellow;}增加让小方块左右移动的动画效果,动画时长还会在后面用到,所以定义成变量:.container span { –duration: 2s; animation: move var(–duration) infinite;}@keyframes move { 0%, 100% { left: calc(10% - 0.5em); } 50% { left: calc(90% - 0.5em); }}用贝赛尔曲线调整动画的时间函数,使小方块看起来就像在左右两侧跳来跳去:.container span { animation: move var(–duration) cubic-bezier(0.6, -0.3, 0.7, 0) infinite;}增加小方块变形的动画,使它看起来有下蹲起跳的拟人效果:.container span { animation: move var(–duration) cubic-bezier(0.6, -0.3, 0.7, 0) infinite, morph var(–duration) ease-in-out infinite;}@keyframes morph { 0%, 50%, 100% { transform: scale(0.75, 1); } 25%, 75% { transform: scale(1.5, 0.5); }}至此,完成了 1 个方块的动画。接下来设置多个方块的动画效果。为子元素定义 CSS 下标变量:.container div:nth-child(1) { –n: 1; }.container div:nth-child(2) { –n: 2; }.container div:nth-child(3) { –n: 3; }.container div:nth-child(4) { –n: 4; }.container div:nth-child(5) { –n: 5; }.container div:nth-child(6) { –n: 6; }.container div:nth-child(7) { –n: 7; }.container div:nth-child(8) { –n: 8; }.container div:nth-child(9) { –n: 9; }旋转子元素,使小方块分布均匀地在容器的四周,围合成一个圆形:.container div { transform: rotate(calc(var(–n) * 40deg));}设置动画延时,现在看起来就像是一群小方块贴着一个圆的内边线在旋转了(但实际上没有任何元素在做旋转运动,大脑感觉到的旋转是一种错觉):.container span { animation-delay: calc(var(–duration) / 9 * var(–n) * -1);}最后,为小方块上色:.container span { background-color: hsl(calc(var(–n) * 80deg), 100%, 70%);}大功告成! ...

September 5, 2018 · 1 min · jiezi

构建自己的博客

一、前言看过很多人,用github创建个人博客,最近抽空也实现的自己的博客,下面就把摸索过程记录下。二、准备安装Node.jsNode.js下载地址:https://nodejs.org/en/download/安装过程一路默认安装即可。详细安装文档参看:http://www.runoob.com/nodejs/…安装Git软件Git软件下载地址:https://git-scm.com/download安装过程一路默认安装即可。关于更多的Git讲解参看:https://www.liaoxuefeng.com/w…安装Hexo什么是 Hexo?Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。Hexo官方网站:https://hexo.io/zh-cn/ ,我用的当前版本是v6.4.0,基本步骤:新建一个blog空文件夹,cmd窗口或vscode终端,输入命令npm install -g hexo-cli全局安装hexo;安装完成后输入hexo -v显示hexo的相关信息说明安装成功;输入命令hexo init初始化hexo项目,安装相关依赖:上一步安装完成,输入命令hexo s或hexo server,开启服务,成功后,在浏览器访问http://localhost:4000生成的默认主题博客。PS:在这里可以npm install hexo-browsersync –save实现热更新,不必每次更改都去刷新浏览器。安装完成后的目录结构 - node_modules // 依赖包 - public // 存放生成的页面 - scaffolds // 生成页模板 - source // 创建的源文章 - themes // 主题 - _config.yml // 博客配置(站点配置) - db.json // 解析source得到的库文件 - package.json // 项目依赖配置 三、修改站点配置_config.yml文件是对整个站点基本信息的配置,比如:# Sitetitle: // 博客名称subtitle: // 副标题description: // 描述 用于seokeywords: // 关键字 用于seoauthor: // 作者 用于seolanguage: // 语言timezone: // 时区四、Github创建一个hexo的代码库和创建其它git仓库一样,只不过名称必须为yourname.github.io这种形式,其中yourname是你github个人账号名,创建好后,找到站点配置文件(blog下的_config.yml文件),找到:# Deployment## Docs: https://hexo.io/docs/deployment.htmldeploy: type:改成你的 博客git仓库地址和分支:deploy: type: git repo: https://github.com/YourgithubName/YourgithubName.github.io.git branch: master这样,远程仓库配置完成。接下来输入命令hexo generate或hexo g将原markedown文章生成静态页面,放置在生成的public目录下;npm install hexo-deployer-git –save安装hexo的git插件;再输入命令hexo deploy或hexo d将生成的静态页面推送到远程仓库;完成后,在浏览器访问https://yourname.github.io/,就能看到你构建好的个人博客啦!五、写文章hexo支持用markdown写作,在目录blog/source/_posts新建markdown文件,或者使用命令hexo new posts 你的文章标题。小坑:{{}}符号编译出错markdown生成静态页面,{{}}是swig模板符号,属于特殊字符,在编译时解析不了双大括号中间字符串就会报错FATAL Something’s wrong. Maybe you can find the solution here: http://hexo.io/docs/troubleshooting.htmlTemplate render error: (unknown path) [Line 3, Column 8] unexpected token: }}解决方案:用转义字符代替{ -> &#123; — 大括号左边部分Left curly brace} -> &#125; — 大括号右边部分Right curly brace六、配置主题hexo默认主题是landscape,样式可能不是每个人都喜爱的,官方页提供了一些主题,可以按自己的风格安装,只需在站点配置文件_config.yml更改主题名称。Next主题是目前比较流行的主题,文档相对比较成熟。next主题文档安装cd bloggit clone https://github.com/theme-next/hexo-theme-next themes/next更换主题# Extensions## Plugins: https://hexo.io/plugins/## Themes: https://hexo.io/themes/theme: next修改Next主题配置文件目录blog/themes/next找到_config.yml文件,其中有很多配置项,下面列举几个常用的。更换头像# Sidebar Avataravatar: # in theme directory(source/images): /images/avatar.gif # in site directory(source/uploads): /uploads/avatar.gif # You can also use other linking images. url: /images/avatar.png # If true, the avatar would be dispalyed in circle. rounded: true # The value of opacity should be choose from 0 to 1 to set the opacity of the avatar. opacity: 1 # If true, the avatar would be rotated with the cursor. rotated: false只需将头像的url换成你自己头像的url或者直接覆盖头像图片即可。注意这里的根/的绝对路径是blog/themes/next/source/,以后写文章引用本地图片的话,注意放到这个目录下!代码高亮NexT使用Tomorrow Theme作为代码高亮,共有5款主题供你选择。 NexT默认使用的是白色的normal主题,可选的值有normal,night,night blue, night bright,night eighties。# Code Highlight theme# Available values: normal | night | night eighties | night blue | night bright# https://github.com/chriskempson/tomorrow-themehighlight_theme: normal添加分类页文章可能需要分类,添加分类页cd bloghexo new page categories此时在blog/source目录下就生成了categories/index.md文件:—title: 分类date: 2018-08-28 14:59:48type: categoriescomments: false // 分类页不需要添加评论—然后,放开主题配置文件_config.yml中menu setting对categories注释menu: home: / || home categories: /categories/ || th以后文章的内容头声明的分类就会在分类页有索引了。添加标签页跟添加分类页一样,文章也需要标签cd bloghexo new page tags此时在blog/source目录下就生成了tags/index.md文件:—title: 标签date: 2018-08-28 14:34:22type: tagscomments: false // 标签页不需要评论—然后,放开主题配置文件_config.yml中menu setting对tags注释menu: home: / || home tags: /tags/ || tags以后文章的内容头声明的分类就会在分类页有分类了。404页当访问当前站点,页面找不到,跳转到404页,推荐用腾讯公益404页面,寻找丢失儿童,让大家一起关注此项公益事业!使用方法,新建404.html页面,放到主题的source目录下,内容如下:<!DOCTYPE HTML><html><head> <meta http-equiv=“content-type” content=“text/html;charset=utf-8;”/> <meta http-equiv=“X-UA-Compatible” content=“IE=edge,chrome=1” /> <meta name=“robots” content=“all” /> <meta name=“robots” content=“index,follow”/> <link rel=“stylesheet” type=“text/css” href=“https://qzone.qq.com/gy/404/style/404style.css"></head><body> <script type=“text/plain” src=“http://www.qq.com/404/search_children.js" charset=“utf-8” homePageUrl=”/” homePageName=“回到我的主页”> </script> <script src=“https://qzone.qq.com/gy/404/data.js" charset=“utf-8”></script> <script src=“https://qzone.qq.com/gy/404/page.js" charset=“utf-8”></script></body></html>站点分析统计对于个人站点,我们需要统计用户访问量,用户分布,跳转率等。Next主题推荐使用的有百度统计、Google分析、腾讯分析了,使用均一样,注册获取站点统计id。百度统计我一直用的百度统计,注册百度统计,管理 > 网站列表 > 新增网站完成后,代码管理 > 代码获取,就能得到统计id。# Baidu Analytics IDbaidu_analytics: 统计id不蒜子统计不蒜子统计可以统计站点以及每个页面的PV、UV和站点总的访问数,以小眼睛的形式展现。编辑主题配置文件中的busuanzi_count的配置项。当enable: true时,代表开启全局开关。若total_visitors、total_views、post_views的值均为false时,不蒜子仅作记录而不会在页面上显示。内容分享服务Next主题还提供了对外提供分享接口,包括百度分享、addthis Share和NeedMoreShare2,要用到哪个,只需把相应enable: true,注册账号获取id即可。评论功能当前版本配置,支持畅言,Disqus,valine,gitment。畅言 - 搜狐提供的评论组件,功能丰富,体验优异,防止注水;但必须进行域名备案。只要域名备过案就可以通过审核。Disqus - 国外使用较多的评论组件。万里长城永不倒,一枝红杏出墙来,你懂的。valine - LeanCloud提供的后端云服务,可用于统计网址访问数据,分为开发版和商用版,只需要注册生成应用App ID和App Key即可使用。Ditment - Gitment 是一款基于GitHub Issues的评论系统。支持在前端直接引入,不需要任何后端代码。可以在页面进行登录、查看、评论、点赞等操作,同时有完整的Markdown / GFM和代码高亮支持。尤为适合各种基于GitHub Pages的静态博客或项目页面。畅言要备案,对于对于挂靠在GitHub的博客非常的不友好,放弃!Disqus,目前国内网络环境访问不了,放弃!valine在用户没有认证登录可以评论,不能防止恶意注水,放弃!我选择用Gitment,让用户用自己的GitHub账号才能评论,用git的一个仓库来存储评论(评论以该仓库的issue形式展现)。gitment配置Gitment的全部配置项如下,# Gitment# Introduction: https://imsun.net/posts/gitment-introduction/gitment: enable: true mint: true # RECOMMEND, A mint on Gitment, to support count, language and proxy_gateway count: true # Show comments count in post meta area lazy: false # Comments lazy loading with a button cleanly: true # Hide ‘Powered by …’ on footer, and more language: zh-CN # Force language, or auto switch by theme github_user: xxx # MUST HAVE, Your Github Username github_repo: xxx # MUST HAVE, The name of the repo you use to store Gitment comments client_id: xxx # MUST HAVE, Github client id for the Gitment client_secret: xxx # EITHER this or proxy_gateway, Github access secret token for the Gitment proxy_gateway: # Address of api proxy, See: https://github.com/aimingoo/intersect redirect_protocol: # Protocol of redirect_uri with force_redirect_protocol when mint enabled开启enable为true就显示评论框了,不过要真正实现评论可用,需要用自己的github账号注册一个应用许可证书,即OAuth application,注册成功就生成了client_id和client_secret。步骤:你的github首页 > settings > Developer settings > OAuth Apps > New oAuth App。填写好相关信息,其中,Homepage URL和Authorization callback URL都写上你的github博客首页地址,比如https://xxx.github.io/,点击Register application即可完成注册,生成Client ID和Client Secret。这样,用户点击评论框右边登入跳转到github授权,允许授权跳转回来就可以评论啦!小坑:有些文章评论初始化弹出validation failed错误因为GitHub的每个issues有两个lables,一个是gitment,另一个是id,id取的是页面pathname,标签长度限定不超过50个字符,而像一般中文标题文章,转义后字符很容易超过50个字符。目录blog/themes/next/layout/_third-party/comments找到文件gitment.swig,在这里我用文章创建时间戳来当作id,这样长度就不会超过50个字符,成功解决!七、总结构建自己的博客并不难,也不需要什么专业代码基础,需要的是耐心而已(┭┮﹏┭┮都是配置)。PS:我的GitHub博客https://wuwhs.github.io 大佬拍轻点 ...

September 5, 2018 · 3 min · jiezi

前端每日实战:125# 视频演示如何用纯 CSS 创作一个失落的人独自行走的动画

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/MqpOdR/可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。https://scrimba.com/p/pEgDAM/czZnbsr源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读定义 dom,容器中包含 3 个元素,分别代表头、身体和脚:<div class=“man”> <span class=“head”></span> <span class=“body”></span> <span class=“feet”></span></div>居中显示:body { margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background: radial-gradient(lightgray 20%, whitesmoke);}定义容器尺寸:.man { width: 12em; height: 33em; font-size: 10px; position: relative;}定义主色:.man { color: white;}画出头部:.head { position: absolute; width: 7em; height: 7em; background-color: currentColor; border-radius: 50%; right: 0;}画出身体:.body { position: absolute; width: 6.2em; height: 14.4em; background-color: currentColor; top: 7em; border-radius: 100% 20% 0 0;}画出脚,现在只能看到一只脚,是因为两只脚重叠在一起,一会儿动起来时就能看到两只脚了:.feet::before,.feet::after { content: ‘’; position: absolute; width: 4em; height: 1.4em; background-color: white; bottom: 0; left: -1.6em; border-radius: 1em 80% 0.4em 0.4em;}用伪元素画出阴影:.man::before { content: ‘’; position: absolute; width: 12em; height: 0.8em; background-color: rgba(0, 0, 0, 0.1); bottom: -0.2em; left: -3em; border-radius: 50%;}接下来增加动画效果。增加行走的动画效果,并使两只脚的动画时间交错:.feet::before,.feet::after { animation: feet-animation 2s ease-in-out infinite;}.feet::after { animation-delay: 1s;}@keyframes feet-animation { 20% { transform: translateX(3.4em) translateY(-1.6em) rotate(4deg); } 30% { transform: translateX(4.6em) translateY(-1em) rotate(0deg); } 40% { transform: translateX(5.6em) translateY(-0.6em) rotate(4deg); } 44% { transform: translateX(5.6em) translateY(0) rotate(0deg); }}增加头和身体起伏的动画效果:.head,.body { animation: body-animation 4s ease-in-out infinite;}@keyframes body-animation { 0%, 100% { transform: translateY(0) skewX(-2deg); } 25%, 75% { transform: translateY(0.5em) skewX(0deg); } 50% { transform: translateY(0) skewX(0deg); }}增加阴影面积随身体运动而变化的动画效果:.man::before { animation: shadow-animate 4s ease-in-out infinite;}@keyframes shadow-animate { 0%, 50%, 100% { transform: scale(1); } 25%, 75% { transform: scale(1.15); }}大功告成! ...

September 3, 2018 · 1 min · jiezi

基于 HTML5 的 WebGL 3D 档案馆可视化管理系统

前言档案管理系统是通过建立统一的标准以规范整个文件管理,包括规范各业务系统的文件管理的完整的档案资源信息共享服务平台,主要实现档案流水化采集功能。为企事业单位的档案现代化管理,提供完整的解决方案,档案管理系统既可以自成系统,为用户提供完整的档案管理和网络查询功能,也可以与本单位的OA办公自动化和DPM设计过程管理,或者与MIS信息管理系统相结合,形成更加完善的现代化信息管理网络。传统档案馆随着社会的快速发展与变化,其内在形式上也发生了巨大变化,逐渐演变为现代智慧档案馆。智慧档案馆以现代科技为依托,充分结合现代物联网技术与云计算技术构建完善的城市智慧档案,实现了现代社会全面管理的目标。本文以当前流行的 H5 技术为主,为现代智慧档案馆提供一套 WEB 解决方案。代码实现场景搭建在本例中,将使用 HT UI 组件对页面实现布局;使用 RelativeLayout 相对布局器将页面分为三个部分:left, top, center,使用 VBoxLayout 纵向布局器将 LEFT 部分分为 logo,editor,chart 三个部分Graph3dView 加载 3D 场景Graph3dView 是 HT 组件中加载 3D 模型的拓扑组件,RelativeLayout 则是 HT 提供的 UI 解决方案,UI 组件中提供 HTView 组件来实现拓扑与 UI 的融合。// 初始化相对布局器var relativeLayout = new ht.ui.RelativeLayout();// 初始化 3D 拓扑var g3dView = new ht.graph3d.Graph3dView();// 初始化 HTVIEW 组件, 并将 3D 拓扑放入其中var htView = new ht.ui.HTView(g3dView);// 布局器加载 HTVIEW 组件relativeLayout.addView(htView, { width: ‘match_parent’, // 填满 height: ‘match_parent’, // 填满 marginTop: 64, // 为 TOP 留下空间 marginLeft: 250 // 为 LEFT 留下空间});创建 LEFT 中的档案袋模型左侧的 EDITOR 部分使用 HT 的调色板组件(ht.widget.Palette), 将档案袋添加到调色板上,并设置为可以拖拽:var palette = new ht.widget.Palette();// palette 面板是将图元都分在“组”里面,然后向“组”中添加图元即可var group = new ht.Group();// 设置分组为打开的状态group.setExpanded(true);// 设置组的说明group.setName(‘基础图元’);palette.dm().add(group);// 添加子图元var childNode = new ht.Node();childNode.setParent(group);childNode.setName(name);childNode.setImage(image);childNode.s({ ‘draggable’: true, // true 为可拖拽 ‘image.stretch’: ‘centerUniform’ // 图片申展方式});palette.dm().add(childNode);实现从调色板中将图元拖拽至 3D 场景在上一步中我们对调色板中的图元属性设置了可拖拽,此时可以实现拖拽图元的动画。但是并不能直接将图元拖拽到 3D 场景中,实现思路是:在拖拽时获取拖拽的图元信息拖拽到对应位置时显示可摆放位置结束拖拽后在对应位置创建对应的 3D 模型对应代码实现如下:拖拽时获取图元信息g3dView.getView().addEventListener(‘dragover’, function(e) { e.preventDefault(); var paletteNode = palette.dm().sm().ld();// 获取 palette 上最后选中的节点 if (!paletteNode || !g3d.getDataAt(e)) return; // 获取鼠标下的节点 var data = g3d.getDataAt(e); if (data.s(‘shape3d’) === ‘档案柜.json’) { // 记录文件袋拖拽到的是哪个档案柜 g3dView._focusData = data; }});拖拽到对应位置时创建 3D 模型,在实际实现过程中由于很难准确地获取到档案柜中每一个可以摆放档案袋的坐标位置,所以在本例中采用了预置的方法。具体原理就是在先创建一个正常不可见的档案柜模型,并在其中将档案袋都摆放完整,在拖拽时,将此不可见的模型与将要摆放的模型重合,此时只需判断鼠标所在的点下是否存在预置的模型存在就可以知道该处是否可以创建 3D 模型,实现效果如下:g3dView.getView().addEventListener(‘dragover’, function(e) { … // 旧逻辑省略 // 拖拽下来的时候设置 所有的 displayName 为 box 的节点 为可见 (这样拖拽才能获取到预置模型) array.forEach(function(data) { data.s(‘3d.visible’, true); }); var data = g3d.getDataAt(e); if (data.s(‘shape3d’) === ‘档案柜.json’) { // 记录文件袋拖拽到的是哪个档案柜 g3dView._focusData = data; // 将预置模型移动到拖拽的柜子坐标 shelf.p3(data.p3()); } if(data.getDisplayName() === ‘box’) { // 将对应坐标下预置的档案袋模型进行显示 // 该属性可修改模型的透明度,更多属性可参考 HT 风格手册 data.s(‘shape3d.opacity’, 0.8); } …})g3dView.getView().addEventListener(‘drop’, function(e) { // 获取鼠标位置模型 var data = g3dView.getDataAt(e); if(!data) return; // 鼠标位置不是预置模型,直接跳过 if(data.getDisplayName() !== ‘box’) return; data.s(‘shape3d.opacity’, 0); // 放手时候设置 所有的 displayName 为 box 的节点 不可见 array.forEach(function(data) { data.s(‘3d.visible’, false); }); var node = new ht.Node(); node.s(‘shape3d’, url); // 档案袋 3D 模型地址 node.r3([Math.PI/2, -Math.PI/2, 0]); // 旋转档案袋模型, 使其平放 node.p3(data.p3()); node.setParent(g3dView._focusData); node.setHost(g3dView._focusData);});档案柜虚化效果实现上面我们已经实现了档案袋拖拽至 3D 场景的效果,但是我们可以发现档案袋模型远小于柜子,要将档案袋摆放到正确的位置并不是那么容易。所以此时我们可以将需要操作的档案柜放大到正中间,其它模型进行虚化处理。// 3D 拓扑交互监听g3dView.mi(function(e){ if(e.kind === ‘doubleClickData’) { // 双击事件 var shape3d = e.data.s(‘shape3d’), parentShape3d = e.data.getParent() && e.data.getParent().s(‘shape3d’); if (shape3d && shape3d.indexOf(‘档案柜’) > -1) { // 重点突出档案柜 showDetail(e.data); } else if (parentShape3d && parentShape3d.indexOf(‘档案柜’) > -1) { showDetail(e.data.getParent()); } }});showDetail = function(data) { // 保存进入虚化状态前 视角 与 中心点 eyeBack = ht.Default.clone(graph3dView.getEye()); centerBack = ht.Default.clone(graph3dView.getCenter()); // 设置相机指向配置 var opt = {}; opt.animation = true; opt.ratio = 1; opt.direction = [1, 0.5, 0]; opt.center = [data.getX(), 100, data.getY()]; graph3dView.flyTo(data, opt); focusData = data; data.s(‘select.brightness’, 1); dataModel.each(function (d) { if (d === focusData || (!d.s(‘3d.selectable’) && d.getTag() !== ‘wall’) || d.getParent() === focusData || d.getDisplayName() === ‘box’) return; // 将拓扑中除了要操作的柜子 与柜子中档案袋 以及墙外 透明度都设置为 opacity (0~1) // 保存设置前配置, 还原用 if (!opacityMap[d.getId()]) { opacityMap[d.getId()] = { ‘shape3d.opacity’: d.s(‘shape3d.opacity’), ‘shape3d.transparent’: d.s(‘shape3d.transparent’), ‘all.opacity’: d.s(‘all.opacity’), ‘all.transparent’: d.s(‘all.transparent’), ’left.opacity’: d.s(’left.opacity’), ’left.transparent’: d.s(’left.transparent’), ‘right.opacity’: d.s(‘right.opacity’), ‘right.transparent’: d.s(‘right.transparent’), ‘front.opacity’: d.s(‘front.opacity’), ‘front.transparent’: d.s(‘front.transparent’), ‘back.opacity’: d.s(‘back.opacity’), ‘back.transparent’: d.s(‘back.transparent’), ’top.opacity’: d.s(’top.opacity’), ’top.transparent’: d.s(’top.transparent’), ‘bottom.opacity’: d.s(‘bottom.opacity’), ‘bottom.transparent’: d.s(‘bottom.transparent’), ‘3d.selectable’: d.s(‘3d.selectable’) } } // 透明度设置为 opacity d.s({ ‘shape3d.opacity’: opacity, ‘shape3d.transparent’: true, ‘all.opacity’: opacity, ‘all.transparent’: true, ’left.opacity’: opacity, ’left.transparent’: true, ‘right.opacity’: opacity, ‘right.transparent’: true, ‘front.opacity’: opacity, ‘front.transparent’: true, ‘back.opacity’: opacity, ‘back.transparent’: true, ’top.opacity’: opacity, ’top.transparent’: true, ‘bottom.opacity’: opacity, ‘bottom.transparent’: true, ‘3d.selectable’: false }); });}退出虚化模式,以监控 3D 拓扑的选中变化来实现g3dView.dm().ms(function(e) { var lastData = g3dView.sm().ld(); // 判断是否进行虚化 if(focusData) { if(lastData === focusData || (lastData && lastData.getParetn() === focusData)) return; g3dView.setEye(eyeBack); g3dView.setCenter(centerBack); // 还原模型的原透明度 g3dView.dm().each(function (d) { if (d === focusData) return; d.s(opacityMap[d.getId()]); }); focusData.s(‘select.brightness’, 0.7); focusData = null; eyeBack = null; centerBack = null; }});快速查询功能实现在 HT 的组件中有提供快速查询插件 QuickFinder ,此次我们就运用该插件来实现简单的档案编号查询// 初始化 输入框var textField = new ht.ui.TextField;textField.setIcon(“imgs/search.json”);textField.setIconPosition(“left”);// 初始化查询器,条件:idvar finder = new ht.QuickFinder(library.view.dm, “id”);// 输入框点击查询按钮时触发textField.on(‘p:value’, function(e) { var dm = library.view.dm; var value = e.newValue; var datas = finder.find(value); // 查询到对应的图元时,我们将第一个结果进行选中 if (datas && datas.size() > 0) { library.view.dm.sm().ss(datas.get(0)); }});总结经过以上功能的实现,一个基础的智慧档案管理系统就成形了。当然做为一个智慧管理系统,这些还是远远不够的,例如档案动态监控、档案室内人员走动监控、视频监控、温度监控、灾害报警等等模块都是后期可以完善的地方。这里只是简单地为大家提供了一个基于 HTML5 WEBGL 的 3D 解决方案。同样原理,智能楼宇、智能机房、智能城市也可以基于此来实现。最终实现链接可查看:https://hightopo.com/demo/int… ...

September 3, 2018 · 3 min · jiezi

前端每日实战:124# 视频演示如何用纯 CSS 创作一只纸鹤(体验三角形造型技巧)

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/xagoYb可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。https://scrimba.com/p/pEgDAM/cPw8eSg源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读定义 dom,容器中包含 6 个元素,分别代表头、颈、身体侧面、翅、尾、胸:<div class=“cranes”> <span class=“head”></span> <span class=“neck”></span> <span class=“side”></span> <span class=“wing”></span> <span class=“tail”></span> <span class=“belly”></span></div>居中显示:body { margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background-color: dodgerblue;}定义容器尺寸:.cranes { width: 52em; height: 50em; font-size: 7px;}设置纸鹤的颜色为白色:.cranes { color: white;}画出头部:.cranes { position: relative;}.head { border-left: 13em solid transparent; border-right: 6em solid transparent; border-bottom: 2em solid; position: absolute; left: 0; top: 21; transform: rotate(-5deg);}把以上创建三角形的代码抽象成一个模板,然后数据都改为变量,类似于调用函数的样子:.cranes span { border-left: calc(var(–left) * 1em) solid transparent; border-right: calc(var(–right) * 1em) solid transparent; border-bottom: calc(var(–bottom) * 1em) solid; position: absolute; transform: rotate(calc(var(–rotation) * 1deg)); left: calc(var(–x) * 1em); top: calc(var(–y) * 1em);}.head { –left: 13; –right: 6; –bottom: 2; –x: 0; –y: 21; –rotation: -5;}设置透明度,以便元素叠加处有折纸效果:.cranes span { filter: opacity(0.6);}接下来就是逐个调用生成三角形的函数创建其他三角形:颈:.neck { –left: 6; –right: 6; –bottom: 12; –x: 14; –y: 19; –rotation: 75;}身体侧面:.side { –left: 1.5; –right: 11.5; –bottom: 20; –x: 18.8; –y: 15.1; –rotation: 20;}翅:.wing { –left: 18.7; –right: 30; –bottom: 8; –x: 6.7; –y: 9.2; –rotation: -41.9;}尾:.tail { –left: 18.6; –right: 7.7; –bottom: 3.9; –x: 19.6; –y: 38.1; –rotation: -126.5;}胸:.belly { –left: 6.2; –right: 1.8; –bottom: 11.5; –x: 17.5; –y: 27.8; –rotation: -99;}至此,纸鹤画完。最后,增加一点交互效果,当鼠标悬停时,由等腰直角三角形变形成鹤:.cranes:hover span { animation: appear 1s ease-in;}@keyframes appear { from { border-left: 3em solid transparent; border-right: 3em solid transparent; border-bottom: 3em solid; position: absolute; transform: rotate(0deg); left: calc((52em - 3em) / 2); top: calc((50em - 3em) / 2); }}大功告成! ...

September 2, 2018 · 2 min · jiezi

前端每日实战 2018 年 8 月份项目汇总(共 29 个项目)

《前端每日实战》专栏每天分解一个前端项目,用视频记录编码过程,再配合详细的代码解读,是学习前端开发的活的参考书!以下是 2018 年 8 月份发布的项目:95# 视频演示如何用纯 CSS 和 D3 创作一只扭动的蠕虫96# 视频演示如何用纯 CSS 和 D3 创作一艘遨游太空的宇宙飞船97# 视频演示如何用纯 CSS 创作一组昂首阔步的圆点98# 视频演示如何用纯 CSS 创作一只愤怒小鸟中的绿猪99# 视频演示如何用纯 CSS 创作一个过山车 loader100# 视频演示如何用纯 CSS 创作闪闪发光的霓虹灯文字#101 视频演示如何用 CSS 和 D3 创作一组摆线102# 视频演示如何用纯 CSS 创作一个小和尚103# 视频演示如何用纯 CSS 创作一只监视眼104# 视频演示如何用纯 CSS 创作一个货车 loader105# 视频演示如何用纯 CSS 创作一只玉免106# 视频演示如何用纯 CSS 创作一个没有 DOM 元素的动画107# 视频演示如何用纯 CSS 创作一只单眼怪兽108# 视频演示如何用 CSS 和 D3 创作一个抽象的黑白交叠动画109# 视频演示如何用 CSS 和 D3 创作一个用文字组成的心形图案110# 视频演示如何用纯 CSS 创作一只愤怒小鸟中的黑炮111# 视频演示如何用纯 CSS 创作一只艺术的鸭子112# 视频演示如何用纯 CSS 创作切换背景的按钮悬停效果113# 视频演示如何用纯 CSS 创作一个赛车 loader114# 视频演示如何用纯 CSS 和混色模式创作一个 loader 动画115# 视频演示如何用 CSS 和 D3 创作一组 数字的彩灯116# 视频演示如何用 VanillaJS 开发一个监控网络连接状态的页面117# 视频演示如何用纯 CSS 创作一只愤怒小鸟中的红火118# 视频演示如何用纯 CSS 创作一个沙漏 loader119# 视频演示如何用纯 CSS 创作一个接扎啤的动画(内含2个视频)[120# 视频演示如何用纯 CSS 创作锡纸撕开的文字效果(https://segmentfault.com/a/11…121# 视频演示如何用 CSS 和 D3 创作一个小鱼游动的交互动画122# 视频演示如何用纯 CSS 创作一个苹果系统的相册图标123# 视频演示如何用纯 CSS 创作一架双冀飞机 ...

September 2, 2018 · 1 min · jiezi

前端每日实战:123# 视频演示如何用纯 CSS 创作一架双冀飞机

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/yxVYRL可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。https://scrimba.com/p/pEgDAM/cmVLRc9源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读定义 dom,容器中包含 3 个子元素,分别表示机冀、螺旋桨和轮子,机冀有 4 片叶片,轮子左右各一只:<div class=“plane”> <div class=“wings”></div> <div class=“fans”> <span></span> <span></span> <span></span> <span></span> </div> <div class=“wheels”> <span class=“left”></span> <span class=“right”></span> </div></div>定义容器尺寸:.plane { width: 28em; height: 13em; font-size: 10px;}定义子元素整体布局和共有属性:.plane { display: flex; justify-content: center; position: relative;}.plane > * { position: absolute;}.plane > *::before,.plane > *::after { content: ‘’; position: absolute;}定义基本色:.plane { color: black;}画出双冀:.wings { width: inherit; display: flex; justify-content: center;}.wings::before { width: inherit; height: 0.5em; background-color: currentColor;}.wings::after { top: 4em; width: 90%; height: 0.4em; background-color: currentColor;}画出螺旋桨的中心:.fans { width: 11em; height: 11em; outline: 1px dashed; background: radial-gradient( black 2.5em, transparent 2.5em );}定义叶片的形状为半圆形:.fans span { width: inherit; height: inherit;}.fans span::before { width: inherit; height: 50%; background-color: rgba(255, 255, 255, 0.4); border-radius: 50% 50% 0 0 / 100% 100% 0 0;}分别旋转叶片的角度,使 4 个页片均匀分布在一个圆内:.fans span::before { transform-origin: bottom; transform: rotate(calc((var(–n) - 1) * 90deg));}.fans span:nth-child(1) { –n: 1;}.fans span:nth-child(2) { –n: 2;}.fans span:nth-child(3) { –n: 3;}.fans span:nth-child(4) { –n: 4;}画出 2 个轮子:.wheels { width: 16em; height: 2em; outline: 1px dashed; bottom: 0; display: flex; justify-content: space-between;}.wheels span { position: static; width: 1em; height: inherit; background-color: currentColor; border-radius: 0.5em;}画出轮子的 2 个支架:.wheels span { display: flex; justify-content: center;}.wheels span::before { width: 0.2em; height: 8em; background-color: currentColor; transform-origin: bottom; bottom: 1em; z-index: -1;}.wheels .left::before { transform: rotate(45deg);}.wheels .right::before { transform: rotate(-45deg);}接下来制作动画效果。增加螺旋桨旋转的动画效果:.fans span { animation: fans-rotating 0.8s linear infinite; animation-delay: calc(var(–n) * 0.1s);}@keyframes fans-rotating { to { transform: rotate(-1turn); }}增加飞机飞行的动画效果:.plane { animation: fly 5s infinite;}@keyframes fly { 10%, 50%, 100% { top: 0; } 25% { top: 1em; } 75% { top: -1em; }}再增加飞机旋转的动画效果:.plane { animation: plane-rotating 10s infinite, fly 5s infinite;}@keyframes plane-rotating { 10%, 30%, 50% { transform: rotate(0deg); } 20% { transform: rotate(-4deg); } 80% { transform: rotate(8deg); } 100% { transform: rotate(-1turn); }}大功告成! ...

September 1, 2018 · 2 min · jiezi

前端每日实战:122# 视频演示如何用纯 CSS 创作一个苹果系统的相册图标

效果预览按下右侧的“点击预览”按钮可以在当前页面预览,点击链接可以全屏预览。https://codepen.io/comehope/pen/zJKwbO可交互视频此视频是可以交互的,你可以随时暂停视频,编辑视频中的代码。请用 chrome, safari, edge 打开观看。https://scrimba.com/p/pEgDAM/cDBZNUW源代码下载每日前端实战系列的全部源代码请从 github 下载:https://github.com/comehope/front-end-daily-challenges代码解读定义 dom,容器中包含 8 个元素,每个元素代表一个矩形色块:<div class=“icon”> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span> <span></span></div>居中显示:body { margin: 0; height: 100vh; display: flex; align-items: center; justify-content: center; background-color: #ccc;}定义容器尺寸:.icon { width: 10em; height: 10em; font-size: 30px; background-color: #eee; border-radius: 20%;}绘制出矩形的轮廓(边框为辅助线,最终会被删除),并放置在容器的中上方:.icon { position: relative; display: flex; justify-content: center; box-sizing: border-box; padding: 1em;}.icon span { position: absolute; width: 22.5%; height: 37.5%; border: 1px dashed black; border-radius: 50% / 30%;}为矩形设置下标变量 –n:.icon span:nth-child(1) { –n: 1;}.icon span:nth-child(2) { –n: 2;}.icon span:nth-child(3) { –n: 3;}.icon span:nth-child(4) { –n: 4;}.icon span:nth-child(5) { –n: 5;}.icon span:nth-child(6) { –n: 6;}.icon span:nth-child(7) { –n: 7;}.icon span:nth-child(8) { –n: 8;}让 8 个矩形依次旋转固定的角度,围合成一个圆形:.icon span { transform-origin: center 105%; transform: rotate(calc((var(–n) - 1) * 45deg));}为矩形设置颜色变量 –c:.icon span:nth-child(1) { –c: rgba(243, 156, 18, 0.7);}.icon span:nth-child(2) { –c: rgba(241, 196, 15, 0.7);}.icon span:nth-child(3) { –c: rgba(46, 204, 113, 0.7);}.icon span:nth-child(4) { –c: rgba(27, 188, 155, 0.7);}.icon span:nth-child(5) { –c: rgba(65, 131, 215, 0.7);}.icon span:nth-child(6) { –c: rgba(102, 51, 153, 0.7);}.icon span:nth-child(7) { –c: rgba(155, 89, 182, 0.7);}.icon span:nth-child(8) { –c: rgba(242, 38, 19, 0.7);}为 8 个矩形上色,并删除掉起辅助线作用的边框:.icon span { /* border: 1px dashed black; */ background-color: var(–c);}设置混色模式,使重叠颜色能叠加在一起:.icon span { mix-blend-mode: multiply;}增加鼠标悬停效果,当悬停时运行矩形色块展开的动画:.icon:hover span { animation: rotating 2s ease-in-out forwards;}@keyframes rotating { from { transform: rotate(0deg); } to { transform: rotate(calc((var(–n) - 1) * 45deg)); }}注意,在动画过程中第 1 个矩形并没有转动,因为它是从 0 度转到 0 度,为了让它转动,要把它的结束角度设置为 360 度,通过修改它的下标变量来实现:.icon span:nth-child(1) { –n: 9;}大功告成! ...

August 30, 2018 · 2 min · jiezi

H5页面在微信端的分享

H5页面在微信端的分享微信分享,咋一看好像很复杂,实则非常简单。只需要调用微信官方出的微信jssdk,加上些许配置,就可以实现h5页面在微信上的分享,官方文档地址为:https://mp.weixin.qq.com/wiki…一、获取基本信息找到已有公众号的appid,根据这个appid和url向后端发起请求,拿到配置所需要的参数:timestamp、noncestr和signature。二、实现1、页面引入JS-SDK文件通过script标签,引入微信官网的JS-SDK文件<script src=“https://res.wx.qq.com/open/js/jweixin-1.2.0.js" type=“text/javascript”></script>2、基本配置wx.config({ debug: false, // 是否开启调试模式 appId: appid, //appid timestamp: timestamp, // 时间戳 nonceStr: noncestr, // 随机字符串 signature: signature, // 签名 jsApiList: [ ‘onMenuShareTimeline’, ‘onMenuShareAppMessage’, ‘onMenuShareQQ’, ‘onMenuShareWeibo’, ‘onMenuShareQZone’ ] // 需要使用的JS接口列表})3、使用wx.ready(function(){ // 分享给好友 wx.onMenuShareAppMessage({ title: title, // 分享标题 desc: desc, // 分享描述 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 success: function () { doShareDone() }, cancel: function () { doShareCancel() } }) // 分享到朋友圈 wx.onMenuShareTimeline({ title: title, // 分享标题 link: link, // 分享链接 imgUrl: imgUrl, // 分享图标 success: function () { doShareDone() }, cancel: function () { doShareCancel() } })})// 分享成功回调function doShareDone () { console.log(‘分享成功’)}// 取消分享回调function doShareCancel () { console.log(‘取消了分享’)}三、调试wx.config里的debug字段设置为true时,就可以进行调试。调试要用到微信开发者工具,选择公众号网页项目,输入页面地址就可以了。四、遇到的问题及解决方案微信JS-SDK说明文档的附录5里有大部分问题的解决方案,在这里我列出我遇到的几个上面没有给出解决方案的。1、Uncaught TypeError: Cannot read property ‘config’ of undefined解决:html页面单独引入了sdk,并且组件统一也引入了一遍sdk,导致问题,删除其中之一。2、Uncaught (in promise) TypeError: Cannot read property ‘ready’ of undefined解决:同问题1。3、invalid signature解决:如果文档里的方法都没有解决这个问题,还有一种方法,先设置一种最基础的配置,使其config ok,然后再设置一遍自己需要的有各种参数的分享文案,这样能绕过配置,成功分享。说的可能有点拗口,简单点理解就是,页面只要有一个config成功的配置,就可以再继续配置其它分享,哪怕这个分享配置的signature无效。 ...

August 30, 2018 · 1 min · jiezi