当前位置: 首页 > news >正文

HS酒店网站建设衡水电子商务网站建设

HS酒店网站建设,衡水电子商务网站建设,网站推广成本,WordPress渗透思路我们经常会遇上动态生成海报的需求#xff0c;而在小程序中#xff0c;生成图片非Canvas莫属。但是在实际工作当中#xff0c;为了追求效率#xff0c;我们会不可避免地去使用一些JS插件#xff0c;而 wxml-to-canvas 就是一款官方推荐且非常优秀的插件#xff0c;它可以…我们经常会遇上动态生成海报的需求而在小程序中生成图片非Canvas莫属。但是在实际工作当中为了追求效率我们会不可避免地去使用一些JS插件而 wxml-to-canvas 就是一款官方推荐且非常优秀的插件它可以轻松地帮你将HTML代码转换成Canvas进而生成可保存分享的图片。 但是wxml-to-canvas是通过静态模板和样式绘制 canvas 进而导出图片需要单独写一份静态模板用于编译对于很多场景还是有些限制比如有时需要将图文混排的富文本内容生成分享图对于这种长度不定内容动态变化的图片生成需求直接利用官方的canvas接口绘制是十分困难的包括但不限于文字换行、表情文字图片混排、文字加粗、子标题等元素都需要一一绘制 我们的目标是实现一个通过wxml节点标记收集元素从而进行编译转换仅依赖wxml直出需要绘制的canvas进而快速实现图片分享为此学习了 wxml2canvas npm包实现了基础功能的wxml2canvas 此文暂不讨论图片类型的wxml转canvas 先看结果 我们针对简单的demo进行处理包含了对块级元素、行内元素、背景色、等简单样式的转换达到 wxml-canvas-image 的一次性处理且不需要重复书写静态代码模板进行编译 下面让我们一步步探求如何实现这个tiny版的wxml2canvas 实现 小程序提供了如下特性可供我们便捷使用 measureText接口能直接测量出文本的宽度SelectorQuery可以查询到节点对应的computedStyle。 同时小程序也存在一些弊端比如 canvas属于原生组件在移动端会置于最顶层通过SelectorQuery只能拿到节点的style而无法获取文本节点的内容 所以我们第一步获取元素就面临两个问题: 1.如何获取需要转成canvas的元素 2.如何拿到获取元素的对应属性样式、节点、内容… 当获取到待收集的元素后就可以将元素绘制到指定的canvas上也就实现了wxml2canvas 所以初始化时需要传入如下参数 /*** element需要渲染的canvas节点* class查找所有类名为exc-c的节点并进行加入绘制队列* limit限定相对位置 ***/ wxml2Canvas({element: over-canvas,options: {class: .exc-c,limit: .limit-r,}, }) 在wxml中需要绘制的元素需添加 exc-c 类名方便对元素进行查找并且如果需要限定相对位置也需要在父级添加 limit-r 类名例如一个文本的位置(left, top) (50, 80)class为panel的节点的位置为(left, top) (20, 40)则文本canvas上实际绘制的位置(x, y) (50 - 20, 80 -40) (30, 40)。如果不传入limit则以实际的位置(x, y) (50, 80) 绘制 对于 wxml 而言如有需要渲染的文本也需要将文本内容通过 data-textxxx 属性的方式进行挂载因为 SelectorQuery 无法获取文本节点的内容但是可以获取到节点的 dataset 属性从而拿到文本内容 wxml 代码如下 view classcontentWxml:/viewview class-box limit-rview class-line exc-c data-typeinline-text data-text这是{{txt}}这是{{txt}}/viewview class-line -blue exc-c data-typeinline-text data-text这是蓝色行内文字试试这是蓝色行内文字试试/viewview class-line exc-c data-typeinline-text data-text这是换行的行内行内行内行内行内行内行内行内行内行行内行内内文字这是换行的行内行内行内行内行内行行内行内内行内行内行内行内文字/viewview class-line -red exc-c data-typeinline-text data-text这是红色行内红色行内红色行内红色行内文字试试这是红色行内红色行内红色行内红色行内文字试试/viewview class-il exc-c data-typetext data-text这是无边距文字这是无边距文字/viewview class-il-2 exc-c data-typetext data-text这是margin文字这是margin文字/viewview class-il-3 exc-c data-typetext data-text这是padding文字这是padding文字/viewview class-il-4 exc-c data-typetext data-text这是居中有背景文字这是居中有背景文字/viewview class-il-5 exc-c data-typetext data-text这是背景文字 data-backgroundrgba(255, 0, 0, 0.4) data-padding0 0 0 0这是背景文字/view/viewview classcontentCanvas:/viewcanvas canvas-idover-canvas class-box/canvas … 上面是需要配置的代码那下面让我看看如何具体实现 1.配置基础的样式 如字体大小、颜色、边距并返回一个函数用来追加配置 2.解析元素 拿到所有需要绘制的元素以及相对的父级元素 3.按照是否涉及换行进行分类绘制 (块级元素 / 行内元素) 4.全部绘制结束时进行 resolve const Wxml2Canvas config {return new Promise(async (resolve, reject) {const appendSet setInit(config); // 配置基础const [render, limit] await getWxml(config.options); // 获取待绘制元素、限制区域const [block, inlineTmp] sortListByTop(render); // 划分块级元素 \ 行内元素appendSet({ limit }); // 追加配置Promise.all([...drawB(block), drawL(inlineTmp)]).then(resolve).catch(reject);}); }; 初始化 在上面代码中通过 setInit 设置 canvas 的基本属性并将可配置的默认参数宽度、字体大小、字体颜色…以及 ctx 维护成一个对外暴露的公共对象最后返回一个函数供使用者更新配置 const DEFAULT_CONFIG {width: 340,TOP: top,FONT_SIZE: 14,PADDING: 0 0 0 0,FONT_COL: #454545,SHADOW_COL: #ffffff,background: #ffffff,font: 14px PingFang SC,STROKECOLOR: white, };export const CACHE_INFO {};export const setInit (config {}) {const info { ...DEFAULT_CONFIG, ...config };const { background, font, width, height, TOP, STROKECOLOR } info;CACHE_INFO.options info;CACHE_INFO.ctx wx.createCanvasContext(info.element, info._this);CACHE_INFO.ctx.font font;CACHE_INFO.ctx.setTextBaseline(TOP);CACHE_INFO.ctx.setStrokeStyle(STROKECOLOR); // 设置基本样式drawRectToCanvas(0, 0, width, height, { fill: background });return arg Object.keys(arg).forEach(i (CACHE_INFO.options[i] arg[i])); }; 获取元素 在 getWxml 中通过小程序提供的 createSelectorQuery 获取到需要绘制元素的节点信息同时也获取父级相对元素的节点信息作为 limit 界定绘制元素的边界 /*** param { object } item 待处理的节点* returns array 解析后的节点信息*/ export const getWxml item {const { options } CACHE_INFO;const { _this, width } options;const query _this? wx.createSelectorQuery().in(_this): wx.createSelectorQuery();const render new Promise(resolve {query.selectAll(item.class).fields({dataset: true,size: true,rect: true,computedStyle: COMPUT_STYLE,},res resolve(res)).exec();});const limit new Promise(resolve {if (!item.limit) resolve({ top: 0, width });query.select(item.limit).fields({dataset: true,size: true,rect: true,},res resolve(res)).exec();});return Promise.all([render, limit]); }; 其中 COMPUT_STYLE 规定了需要查找的元素样式名称 export const COMPUT_STYLE [width,height,font,fontSize,fontFamily,fontWeight,fontStyle,textAlign,color,lineHeight,border,borderColor,borderStyle,borderWidth,verticalAlign,boxShadow,background,backgroundColor,backgroundImage,backgroundPosition,backgroundSize,paddingLeft,paddingTop,paddingRight,paddingBottom, ]; 获取到相对父级元素将获取的节点作为边界信息更新到配置中 appendSet({ limit }); // 追加配置 获取到的元素可以分为两类 即 涉及文字换行的元素 行内元素不涉及文字换行的元素 块级元素 我们可以通过在书写 wxml 时通过事先声明 data-typeinline-text 这样获取到节点之后可以通过 dataset 获取元素的类型如果为行内元素时需要将行内元素通过高度进行分层将同一行的元素进行归类 /*** param { array } list 待处理的节点* returns array*/ export const sortListByTop list {const [arrBlock, arrLine, lineTemp] [[], [], {}];list.forEach(i {if (i.dataset.type i.dataset.type.indexOf(inline) -1) {arrBlock.push(i);} else {arrLine.push(i);}});arrLine.forEach(i {lineTemp[i.top] lineTemp[i.top] || [];lineTemp[i.top].push(i);});return [arrBlock, lineTemp]; }; 处理元素 通过 drawWxmlBlock 和 drawWxmlInline 分别对元素进行处理和绘制在此之前还需要 drawAfter 方法将节点信息文字、位置… 元素的样式信息padding、width… 转化为对应的 canvas 位置信息: drawAfter /*** 返回节点的真实位置** param { object } el 需要渲染的节点* param {*} leftOffset 从左侧开始绘制的起点* param {*} maxWidth 一行文本的最大宽度* returns 返回canvas位置*/ const drawAfter (el, leftOffset, maxWidth) {const { options } CACHE_INFO;const { left: limitLeft 0, top: limitTop 0 } options.limit;const leftFix el.dataset.left || 0;const topFix el.dataset.top || 0;el.width transferNum(el.width);el.height transferNum(el.height);el.left transferNum(el.left) - limitLeft leftFix;el.top transferNum(el.top) - limitTop topFix;let padding el.dataset.padding || options.PADDING;if (typeof padding string) {padding transferPadding(padding);}const paddingTop el.paddingTop.replace(px, ) padding[0];const paddingRight el.paddingRight.replace(px, ) padding[1];const paddingBottom el.paddingBottom.replace(px, ) padding[2];const paddingLeft el.paddingLeft.replace(px, ) padding[3];el.padding [paddingTop, paddingRight, paddingBottom, paddingLeft];const text el.dataset.text || ;el.background el.dataset.background || el.backgroundColor;return {text,x: leftOffset || el.left,y: el.top,originX: el.left,...(leftOffset { leftOffset }),...(maxWidth { maxWidth }),}; }; drawWxmlBlock: 对于块级元素将转为 canvas 位置信息的节点依次绘制通过 Promise 将绘制完成的结果进行返回 /*** 绘制块级元素* param { array } block 需要绘制的块级元素*/ export const drawWxmlBlock (block []) {return block.map(el new Promise((resolve, reject) {const textData drawAfter(el);drawText(textData, el, text, resolve, reject,);})); }; drawWxmlInline: 对于行内元素首先通过同一个行元素的左右边距计算出一行的最大宽度用于换行并且记录距离左侧的边距 leftOffset 每次绘制完更新下一次绘制的起点从上次结束的位置继续绘制全部绘制完成后进行 resolve 有些贪心算法的影子 /*** 绘制行内元素* param { object } inline 需要绘制的行内元素*/ export const drawWxmlInline (inline {}) {let leftOffset 0;return new Promise(resolve {let maxWidth 0;let minLeft Infinity;let maxRight -Infinity;Object.keys(inline).forEach(top {inline[top].forEach(el {minLeft Math.min(el.left, minLeft);maxRight Math.max(el.right, maxRight);});});// 找出同一top下的最小left和最大right得到最大的宽度用于换行maxWidth Math.ceil(maxRight - minLeft);Object.keys(inline).forEach(top {inline[top].forEach(el {const textData drawAfter(el, leftfOfset, maxWidth);const drawRes drawText(textData, el, inline);leftOffset drawRes.leftOffset; // 每次绘制从上次结束地方开始});});resolve();}); }; 渲染节点 无论是块级元素还是行内元素都是通过 drawText 方法最终渲染节点到指定的 canvas 中只不过传参会发生变化块级元素 每次绘制都是独立的所以每次绘制成功之后会触发 resolve 的回调但是行内元素每次绘制需要返回当前绘制结束之后的定位以此作为下次绘制的起始位置。 块级元素 /*** 绘制文字 * param { object } textData 节点位置* param { object } el 节点信息* param { string } type 渲染节点类型* param { function } resolve 成功时候抛出* param { function } reject 失败时候抛出*/ const DrawTxt (textData, el, type, resolve, reject) {const { ctx, options } CACHE_INFO;try {...[x, y] setTxtAlign(textData, el, textWidth, width);ctx.fillText(textData.text, x, y);ctx.draw(true);resolve();} catch (e) {reject reject(e);} }; 以上对于块级元素时我们只需要结合 textAlign 和 padding 就可以计算出 x 的值通过 行数 * 行高 和 padding 计算出 y 的值最后通过 ctx.fillText 进行绘制 行内元素 /*** 绘制文字 * param { object } textData 节点位置* param { object } el 节点信息*/ const DrawTxt (textData, el) {const { ctx, options } CACHE_INFO;...// 元素换行的情况const maxw textData.maxWidth; // 最大宽度let lineNum Math.max(Math.floor(textWidth / maxw), 1); // 最大行数const singleLength Math.floor(text.length / lineNum); // 每行字数const widthOffset textData.leftOffset - textData.originX; // 计算真实的左边距let [endIdx, fSingle, fsWidth] getTextSingleLine(text, maxw, singleLength, 0, widthOffset);[x, y] setTxtAlign(textData, el, fsWidth);ctx.fillText(fSingle, x, y); // 绘制leftOffset x fsWidth; // 更新左边距topOffset y; // 更新右边距...ctx.draw(true);return { leftOffset, topOffset } }; 上面行内元素需要关心的是 getTextSingleLine 方法即计算截取换行文字的索引和位置通过 offset 矫正每行的真实文本数如果 真实文本距离 左边距 一行最大长度 则不断进行截取并且更新一行的文本字数直到不超过一行最大长度时返回截取的文字索引、截取的文字、截取后的文本长度 getTextSingleLine /*** 当文本超过宽度时计算每一行应该绘制的文本** param {*} text 需要绘制的文字* param {*} width 一行最大长度* param {*} singleLength 每行实际字数* param {*} currentIndex 文字的起始位置索引* param {*} widthOffset 左边距*/ export const getTextSingleLine ( text,width,singleLength,currentIndex 0,widthOffset 0 ) {let offset 0;let endIndex currentIndex singleLength offset;let single text.substring(currentIndex, endIndex);let singleWidth measureWidth(single);while (Math.round(widthOffset singleWidth) width) {offset - 1;endIndex currentIndex singleLength offset;single text.substring(currentIndex, endIndex);singleWidth measureWidth(single);}return [endIndex, single, singleWidth]; }; 最后 最近还整理一份JavaScript与ES的笔记一共25个重要的知识点对每个知识点都进行了讲解和分析。能帮你快速掌握JavaScript与ES的相关知识提升工作效率。 有需要的小伙伴可以点击下方卡片领取无偿分享
http://www.hkea.cn/news/14370354/

相关文章:

  • 建设工程新工艺网站做毕设好的网站
  • 瑞安网站设计网络媒体设计与制作
  • 最优秀的无锡网站建设宁河网站建设
  • 网站备案号申请濮阳做网站
  • 环保设备在那个网站做商标设计logo图案设计软件
  • 成都网站建设服务公司关键词百度云
  • 成都高新区网站建设wordpress安装md
  • 做湘菜的网站安卓手机app应用开发
  • 株洲网站建设开发icp备案网站接入信息ip地址段怎么填
  • 如何做阿里巴巴网站手机qq浏览器网页安全防护怎么关
  • 广州 网站开发 骏域什么网站推广比较好
  • 网站规划详细设计怎么写wordpress 代码在哪里修改
  • 网站建设应遵守的原则企业vi设计欣赏
  • 网上做题扣分在哪个网站上做郑州seo培训班
  • 用asp.net做购物网站北湖区网站建设专业
  • 宜春做网站的数据库内容进 wordpress
  • 吉林省软环境建设网站微信推广文案范文
  • 网站推广存在的问题网站建设论文选题
  • 公司一定建设网站我要推广
  • 一诺建站新媒体营销有哪些岗位
  • 用家用路由器ip做网站WordPress前端文本图片添加
  • 校园服装网站建设预算网站建设话术分析
  • 做网站销售会遇到哪些问题烟台专门做网站的
  • 网站建设中采用的技术方案嘉兴网站建设网站建设
  • 网站建设策划书ppt郑州网站制作工作室
  • 百度网站公司信息推广怎么做的承接网站开发文案
  • 我国网站开发行业门户网站程序
  • 网站的技术维护一般要做些什么seo与sem的关系
  • 钓鱼网站下载页面设计模板网站
  • 专业网站制作价格wordpress安装无法创建目录.