摘要:介绍转换意思是将小程序不支持的东西转换成它支持的东西。我在开发的小程序的过程中遇到了两种需要做转换的场景转换成转换成我将在下文详细介绍我是怎么处理这两种情况的。总结以上,就是我在开发小程序中对与做的一些转换的经历。
介绍
“转换” 意思是将"小程序"不支持的东西转换成它支持的东西。我在开发的小程序的过程中遇到了两种需要做“转换”的场景:
html 转换成 wxml
svg 转换成 canvas
我将在下文详细介绍我是怎么处理这两种情况的。
html 转换成 wxml我们的产品在某些场景下,后端接口会直接传 html 字符串给前端。在 ReactJs 中,我们可以用 dangerouslySetInnerHTML 直接渲染 html 字符串(不一定安全),而 ”小程序“不支持 html ,因此必须对 html 进行处理。解决这个问题的步骤主要是:1. 将 html 转换成 json ( 树结构) ;2. 将 json 转换成 wxml 。我在对问题做了调研后发现,现有一个库 wxParse 满足该转换的目的,但是在我看来,这个库做的事情太多,需要依赖文件过多,不满足只需要简单处理的需要,所以我决定自己写。
html 转换成 json在参考了 html2json 与 himalaya 两个库的处理思路的基础上,我写了一个简单的解析库 htmlParser 。htmlParser 处理 html字符串分两步:
lexer: 生成标记(token)
function lex(html) { let string = html let tokens = [] while (string) { // 先处理以 "" 开始的结束标签 if (string.indexOf("") === 0) { const match = string.match(REGEXP.endTag) if (!match) continue // 通过 substring 截断这个标签的字符串长度 string = string.substring(match[0].length) tokens.push({ tag: match[1], type: "tag-end", }) continue } // 处理以 "<" 开始的标签 if (string.indexOf("<") === 0) { const match = string.match(REGEXP.startTag) if (!match) continue string = string.substring(match[0].length) const tag = match[1] const isEmpty = !!MAKER.empty[tag] const type = isEmpty ? "tag-empty" : "tag-start" const attributes = getAttributes(match[2]) tokens.push({ tag, type, attributes }) continue } // 每个处理过程的其他部分字符串被当做 "text" 文本处理(暂时不处理其他情况) const index = string.indexOf("<") const text = index < 0 ? string : string.substring(0, index) string = index < 0 ? "" : string.substring(index) tokens.push({ type: "text", text }) } return tokens }
parser: 根据标记生成树
上面的 lexer 将 html 字符串分隔成了一个一个 token,然后,我们通过遍历所有的标识来构建树
function parse(tokens) { let root = { tag: "root", children: [] } let tagArray = [root] tagArray.last = () => tagArray[tagArray.length - 1] for (var i = 0; i < tokens.length; i++) { const token = tokens[i] if (token.type === "tag-start") { // 构建节点 const node = { type: "Element", tagName: token.tag, attributes: Object.assign({}, { class: token.tag }, token.attributes), children: [] } tagArray.push(node) continue } if (token.type === "tag-end") { let parent = tagArray[tagArray.length - 2] let node = tagArray.pop() // 将该节点加入父节点中 parent.children.push(node) continue } if (token.type === "text") { // 往该节点中加入子元素 tagArray.last().children.push({ type: "text", content: replaceMark(token.text) }) continue } if (token.type === "tag-empty") { // 往该节点中加入子元素 tagArray.last().children.push({ type: "Element", tagName: token.tag, attributes: Object.assign({}, { class: token.tag }, token.attributes), }) continue } } return root }
整个程序的运行结果举例:
var html = "" htmlParser(html) # 转换结果 { "tag": "root", "children": [{ "type": "Element", "tagName": "div", "attributes": { "style": "height:10rpx;width: 20rpx;" }, "children": [ { "type": "Element", "tagName": "img", "attributes": { src: "http://xxx.jpg", class: "image" } }] }] }
以上,我们完成了 html字符串的转换,完整代码请戳 htmlParser
json 转换成 wxml在熟悉了“小程序”框架的基础上,发现需要借助模板 template ,将 json 数据填充进 template,并根据元素类型渲染相应的 wxml 组件以达到转换目的。比如:
# 定义一个名称为 html-image 的模板/* 使用模板 其中 json 的结构为: { "type": "Element", "tagName": "img", "attributes": { src: "http://xxx.jpg", class: "image" } } */
这样,我们就能转化成功了。
而因为模板没有引用自身的能力,只能使用笨办法:使用多个同样内容,但是模板名称不一样的模板来解决嵌套的层级关系,而嵌套的层级取决于使用的模板个数。
{{content}} // html 引用 html1 两个模板一样
如上处理过程中,有些需要注意的细节,比如:要对 html 实体字符转换,让模板的 image 组件支持 mode 等等。总之,经过如上的处理,html 字符串对 wxml 组件的转换基本功能完成。
svg 转换成 canvas在我们的产品 web 版本中,由于需要在页面元素中使用 svg 作为 dom 元素,而“小程序” 没有 svg 组件的支持,如此一来,我们也需要对后端接口传来的 svg 字符串做转换。“小程序”没有svg 组件但是有 canvas 组件,于是我决定使用 canvas 来模拟 svg 绘制图形,并将图形做一定的修改以满足基本需求。
做这个“转换”的关键也有两点:1. 提取 svg 字符串中的元素;2.canvas 模拟元素功能进行绘制
svg 元素的提取因为 svg 字符串是一个 xml, 用上面的 htmlParser 可以将其生成 json ,问题解决。
canvas 模拟绘制在 web 中 svg 的元素有很多,好在我们需要的只有一些基本的元素:image, rect, path。rect 用 canvas 模拟不算难事,canvas 绘制起来很简单,代码如下:
// draw rect ctx.save() ctx.setFillStyle(attr.fill) ctx.fillRect(attr.x, attr.y, attr.width, attr.height) ctx.restore()
然而,在开发过程中,遇到了一个难点:不知道对 path 的 d 属性如何进行模拟。d 属性涉及移动、贝塞尔曲线等等。比如:
/** * svg path ** d 属性值 "M250 150 L150 350 L350 350 Z" * 我们提取属性的的结构为: [ * { marker: "M", values: [250, 150]} * ] * https://gist.github.com/shamansir/0ba30dc262d54d04cd7f79e03b281505 * 以下代码为 d 属性的提取部分,已在源代码基础上修改, */ _pathDtoCommands(str) { let results = [], match; while ((match = markerRegEx.exec(str)) !== null) { results.push(match) } return results .map((match) => { return { marker: str[match.index], index: match.index } }) .reduceRight((all, cur) => { let chunk = str.substring(cur.index, all.length ? all[all.length - 1].index : str.length); return all.concat([{ marker: cur.marker, index: cur.index, chunk: (chunk.length > 0) ? chunk.substr(1, chunk.length - 1) : chunk }]) }, []) .reverse() .map((command) => { let values = command.chunk.match(digitRegEx); return { marker: command.marker, values: values ? values.map(parseFloat) : [] }; }) }
完成了如上的步骤后,图形基本绘制出来了,但是在后期,出现了 svg image 位置的问题。svg 中的图片除了会有 x, y 坐标关系,还会根据视窗大小,以短边为准,保持宽高比,长边做缩放,视窗中居中显示。这是我之前不清楚的部分,为此多花了点时间和精力。此外,还有些细节需要注意,比如需要调整 canvas 的缩放比例,以让图形完全显示。
总结以上,就是我在开发“小程序”中对 html 与 svg 做的一些“转换”的经历。总结起来就是,对字符串解析,转换成“小程序”语言。在此延伸一下,如需在 wxml 中支持 wxml 字符串,借助 htmlParser 做解析,再写一个 wxml 模板,我们也就能“转换” wxml 了。
参考小程序官方文档
wxParse
html2json
himalaya
svg path 属性
svg d 属性值解析
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88221.html
在微信小程序开发中用新功能利用uni-app来开发,我们看看都有哪些优缺? 首选我们看看官网给出的解决思路方案 https://uniapp.dcloud.io/hybrid 方式1:把原生小程序转换为uni-app源码。有各种转换工具,详见 方式2:新建一个uni-app项目,把原生小程序的代码变成小程序组件,进而整合到uni-app项目下。uni-app支持使用小程序wxml组件,...
摘要:我的目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。它导致了数不清的错误漏洞和系统崩溃,可能在之后年中造成了十亿美元的损失。这个函数将使用一个表示我们希望进行转换的函数参数,并返回一个包含转换结果的新参数。 翻译原文出处:Building a Maybe in JavaScript 鄙人翻译略差且略有出入,别见笑。 很多时候我们会碰到:Uncaught TypeError...
众所周知,Python的一个使用场景还是比较多的,在工作当中,也会涉及到多方面的一些事情。那么,今天小编写这篇文章的一个主要目的,给大家来介绍关于如何用Python完成百度与搞得地图转换,下面就给大家详细介绍下。 一、地理编码与逆编码 地理编码与逆编码表示的是地名地址与地理坐标(经纬度)互相转换的过程。其中,将地址信息映射为地理坐标的过程称之为地理编码;将地理坐标转换为地址信息的过程称之为...
阅读 953·2021-11-25 09:43
阅读 2290·2019-08-30 15:55
阅读 3152·2019-08-30 15:44
阅读 2052·2019-08-29 16:20
阅读 1452·2019-08-29 12:12
阅读 1608·2019-08-26 12:19
阅读 2282·2019-08-26 11:49
阅读 1711·2019-08-26 11:42