分享海报
This commit is contained in:
parent
b90d67fcd5
commit
3f8653b337
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,4 +2,5 @@
|
||||
/.git
|
||||
*.log
|
||||
project.private.config.json
|
||||
project.config.json
|
||||
project.config.json
|
||||
node_modules
|
||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -3,6 +3,6 @@
|
||||
"*.wxss": "css",
|
||||
"*.tpl": "html",
|
||||
"*.vue": "vue",
|
||||
"*.wxml": "html"
|
||||
"*.wxml": "wxml"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,779 +0,0 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define([], factory);
|
||||
else {
|
||||
var a = factory();
|
||||
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||
}
|
||||
})(window, function() {
|
||||
return /******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
const hex = (color) => {
|
||||
let result = null
|
||||
|
||||
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||
return color
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||
return '#' + result[2].split(',').map((part, index) => {
|
||||
part = part.trim()
|
||||
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||
part = part.toString(16)
|
||||
if (part.length === 1) {
|
||||
part = '0' + part
|
||||
}
|
||||
return part
|
||||
}).join('')
|
||||
} else {
|
||||
return '#00000000'
|
||||
}
|
||||
}
|
||||
|
||||
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||
if (index === 0) {
|
||||
return part
|
||||
}
|
||||
return part[0].toUpperCase() + part.slice(1)
|
||||
}).join('')
|
||||
|
||||
const compareVersion = (v1, v2) => {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hex,
|
||||
splitLineToCamelCase,
|
||||
compareVersion
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
|
||||
const xmlParse = __webpack_require__(2)
|
||||
const {Widget} = __webpack_require__(3)
|
||||
const {Draw} = __webpack_require__(5)
|
||||
const {compareVersion} = __webpack_require__(0)
|
||||
|
||||
const canvasId = 'weui-canvas'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
width: {
|
||||
type: Number,
|
||||
value: 400
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
value: 300
|
||||
}
|
||||
},
|
||||
data: {
|
||||
use2dCanvas: false, // 2.9.2 后可用canvas 2d 接口
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const {SDKVersion, pixelRatio: dpr} = wx.getSystemInfoSync()
|
||||
const use2dCanvas = compareVersion(SDKVersion, '2.9.2') >= 0
|
||||
this.dpr = dpr
|
||||
this.setData({use2dCanvas}, () => {
|
||||
if (use2dCanvas) {
|
||||
const query = this.createSelectorQuery()
|
||||
query.select(`#${canvasId}`)
|
||||
.fields({node: true, size: true})
|
||||
.exec(res => {
|
||||
const canvas = res[0].node
|
||||
const ctx = canvas.getContext('2d')
|
||||
canvas.width = res[0].width * dpr
|
||||
canvas.height = res[0].height * dpr
|
||||
ctx.scale(dpr, dpr)
|
||||
this.ctx = ctx
|
||||
this.canvas = canvas
|
||||
})
|
||||
} else {
|
||||
this.ctx = wx.createCanvasContext(canvasId, this)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async renderToCanvas(args) {
|
||||
const {wxml, style} = args
|
||||
const ctx = this.ctx
|
||||
const canvas = this.canvas
|
||||
const use2dCanvas = this.data.use2dCanvas
|
||||
|
||||
if (use2dCanvas && !canvas) {
|
||||
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'))
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, this.data.width, this.data.height)
|
||||
const {root: xom} = xmlParse(wxml)
|
||||
|
||||
const widget = new Widget(xom, style)
|
||||
const container = widget.init()
|
||||
this.boundary = {
|
||||
top: container.layoutBox.top,
|
||||
left: container.layoutBox.left,
|
||||
width: container.computedStyle.width,
|
||||
height: container.computedStyle.height,
|
||||
}
|
||||
const draw = new Draw(ctx, canvas, use2dCanvas)
|
||||
await draw.drawNode(container)
|
||||
|
||||
if (!use2dCanvas) {
|
||||
await this.canvasDraw(ctx)
|
||||
}
|
||||
return Promise.resolve(container)
|
||||
},
|
||||
|
||||
canvasDraw(ctx, reserve) {
|
||||
return new Promise(resolve => {
|
||||
ctx.draw(reserve, () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
canvasToTempFilePath(args = {}) {
|
||||
const use2dCanvas = this.data.use2dCanvas
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const {
|
||||
top, left, width, height
|
||||
} = this.boundary
|
||||
|
||||
const copyArgs = {
|
||||
x: left,
|
||||
y: top,
|
||||
width,
|
||||
height,
|
||||
destWidth: width * this.dpr,
|
||||
destHeight: height * this.dpr,
|
||||
canvasId,
|
||||
fileType: args.fileType || 'png',
|
||||
quality: args.quality || 1,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
}
|
||||
|
||||
if (use2dCanvas) {
|
||||
delete copyArgs.canvasId
|
||||
copyArgs.canvas = this.canvas
|
||||
}
|
||||
wx.canvasToTempFilePath(copyArgs, this)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Expose `parse`.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given string of `xml`.
|
||||
*
|
||||
* @param {String} xml
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function parse(xml) {
|
||||
xml = xml.trim()
|
||||
|
||||
// strip comments
|
||||
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
||||
|
||||
return document()
|
||||
|
||||
/**
|
||||
* XML document.
|
||||
*/
|
||||
|
||||
function document() {
|
||||
return {
|
||||
declaration: declaration(),
|
||||
root: tag()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declaration.
|
||||
*/
|
||||
|
||||
function declaration() {
|
||||
const m = match(/^<\?xml\s*/)
|
||||
if (!m) return
|
||||
|
||||
// tag
|
||||
const node = {
|
||||
attributes: {}
|
||||
}
|
||||
|
||||
// attributes
|
||||
while (!(eos() || is('?>'))) {
|
||||
const attr = attribute()
|
||||
if (!attr) return node
|
||||
node.attributes[attr.name] = attr.value
|
||||
}
|
||||
|
||||
match(/\?>\s*/)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag.
|
||||
*/
|
||||
|
||||
function tag() {
|
||||
const m = match(/^<([\w-:.]+)\s*/)
|
||||
if (!m) return
|
||||
|
||||
// name
|
||||
const node = {
|
||||
name: m[1],
|
||||
attributes: {},
|
||||
children: []
|
||||
}
|
||||
|
||||
// attributes
|
||||
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
||||
const attr = attribute()
|
||||
if (!attr) return node
|
||||
node.attributes[attr.name] = attr.value
|
||||
}
|
||||
|
||||
// self closing tag
|
||||
if (match(/^\s*\/>\s*/)) {
|
||||
return node
|
||||
}
|
||||
|
||||
match(/\??>\s*/)
|
||||
|
||||
// content
|
||||
node.content = content()
|
||||
|
||||
// children
|
||||
let child
|
||||
while (child = tag()) {
|
||||
node.children.push(child)
|
||||
}
|
||||
|
||||
// closing
|
||||
match(/^<\/[\w-:.]+>\s*/)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Text content.
|
||||
*/
|
||||
|
||||
function content() {
|
||||
const m = match(/^([^<]*)/)
|
||||
if (m) return m[1]
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute.
|
||||
*/
|
||||
|
||||
function attribute() {
|
||||
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
||||
if (!m) return
|
||||
return {name: m[1], value: strip(m[2])}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip quotes from `val`.
|
||||
*/
|
||||
|
||||
function strip(val) {
|
||||
return val.replace(/^['"]|['"]$/g, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Match `re` and advance the string.
|
||||
*/
|
||||
|
||||
function match(re) {
|
||||
const m = xml.match(re)
|
||||
if (!m) return
|
||||
xml = xml.slice(m[0].length)
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* End-of-source.
|
||||
*/
|
||||
|
||||
function eos() {
|
||||
return xml.length == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for `prefix`.
|
||||
*/
|
||||
|
||||
function is(prefix) {
|
||||
return xml.indexOf(prefix) == 0
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parse
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 3 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
const Block = __webpack_require__(4)
|
||||
const {splitLineToCamelCase} = __webpack_require__(0)
|
||||
|
||||
class Element extends Block {
|
||||
constructor(prop) {
|
||||
super(prop.style)
|
||||
this.name = prop.name
|
||||
this.attributes = prop.attributes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Widget {
|
||||
constructor(xom, style) {
|
||||
this.xom = xom
|
||||
this.style = style
|
||||
|
||||
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color']
|
||||
}
|
||||
|
||||
init() {
|
||||
this.container = this.create(this.xom)
|
||||
this.container.layout()
|
||||
|
||||
this.inheritStyle(this.container)
|
||||
return this.container
|
||||
}
|
||||
|
||||
// 继承父节点的样式
|
||||
inheritStyle(node) {
|
||||
const parent = node.parent || null
|
||||
const children = node.children || {}
|
||||
const computedStyle = node.computedStyle
|
||||
|
||||
if (parent) {
|
||||
this.inheritProps.forEach(prop => {
|
||||
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop]
|
||||
})
|
||||
}
|
||||
|
||||
Object.values(children).forEach(child => {
|
||||
this.inheritStyle(child)
|
||||
})
|
||||
}
|
||||
|
||||
create(node) {
|
||||
let classNames = (node.attributes.class || '').split(' ')
|
||||
classNames = classNames.map(item => splitLineToCamelCase(item.trim()))
|
||||
const style = {}
|
||||
classNames.forEach(item => {
|
||||
Object.assign(style, this.style[item] || {})
|
||||
})
|
||||
|
||||
const args = {name: node.name, style}
|
||||
|
||||
const attrs = Object.keys(node.attributes)
|
||||
const attributes = {}
|
||||
for (const attr of attrs) {
|
||||
const value = node.attributes[attr]
|
||||
const CamelAttr = splitLineToCamelCase(attr)
|
||||
|
||||
if (value === '' || value === 'true') {
|
||||
attributes[CamelAttr] = true
|
||||
} else if (value === 'false') {
|
||||
attributes[CamelAttr] = false
|
||||
} else {
|
||||
attributes[CamelAttr] = value
|
||||
}
|
||||
}
|
||||
attributes.text = node.content
|
||||
args.attributes = attributes
|
||||
const element = new Element(args)
|
||||
node.children.forEach(childNode => {
|
||||
const childElement = this.create(childNode)
|
||||
element.add(childElement)
|
||||
})
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Widget}
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = require("widget-ui");
|
||||
|
||||
/***/ }),
|
||||
/* 5 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
class Draw {
|
||||
constructor(context, canvas, use2dCanvas = false) {
|
||||
this.ctx = context
|
||||
this.canvas = canvas || null
|
||||
this.use2dCanvas = use2dCanvas
|
||||
}
|
||||
|
||||
roundRect(x, y, w, h, r, fill = true, stroke = false) {
|
||||
if (r < 0) return
|
||||
const ctx = this.ctx
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
|
||||
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
|
||||
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
|
||||
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
|
||||
ctx.lineTo(x, y + r)
|
||||
if (stroke) ctx.stroke()
|
||||
if (fill) ctx.fill()
|
||||
}
|
||||
|
||||
drawView(box, style) {
|
||||
const ctx = this.ctx
|
||||
const {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
const {
|
||||
borderRadius = 0,
|
||||
borderWidth = 0,
|
||||
borderColor,
|
||||
color = '#000',
|
||||
backgroundColor = 'transparent',
|
||||
} = style
|
||||
ctx.save()
|
||||
// 外环
|
||||
if (borderWidth > 0) {
|
||||
ctx.fillStyle = borderColor || color
|
||||
this.roundRect(x, y, w, h, borderRadius)
|
||||
}
|
||||
|
||||
// 内环
|
||||
ctx.fillStyle = backgroundColor
|
||||
const innerWidth = w - 2 * borderWidth
|
||||
const innerHeight = h - 2 * borderWidth
|
||||
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
|
||||
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
async drawImage(img, box, style) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const ctx = this.ctx
|
||||
const canvas = this.canvas
|
||||
|
||||
const {
|
||||
borderRadius = 0
|
||||
} = style
|
||||
const {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
ctx.save()
|
||||
this.roundRect(x, y, w, h, borderRadius, false, false)
|
||||
ctx.clip()
|
||||
|
||||
const _drawImage = (img) => {
|
||||
if (this.use2dCanvas) {
|
||||
const Image = canvas.createImage()
|
||||
Image.onload = () => {
|
||||
ctx.drawImage(Image, x, y, w, h)
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
Image.onerror = () => { reject(new Error(`createImage fail: ${img}`)) }
|
||||
Image.src = img
|
||||
} else {
|
||||
ctx.drawImage(img, x, y, w, h)
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
const isTempFile = /^wxfile:\/\//.test(img)
|
||||
const isNetworkFile = /^https?:\/\//.test(img)
|
||||
|
||||
if (isTempFile) {
|
||||
_drawImage(img)
|
||||
} else if (isNetworkFile) {
|
||||
wx.downloadFile({
|
||||
url: img,
|
||||
success(res) {
|
||||
if (res.statusCode === 200) {
|
||||
_drawImage(res.tempFilePath)
|
||||
} else {
|
||||
reject(new Error(`downloadFile:fail ${img}`))
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
reject(new Error(`downloadFile:fail ${img}`))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject(new Error(`image format error: ${img}`))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
drawText(text, box, style) {
|
||||
const ctx = this.ctx
|
||||
let {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
let {
|
||||
color = '#000',
|
||||
lineHeight = '1.4em',
|
||||
fontSize = 14,
|
||||
textAlign = 'left',
|
||||
verticalAlign = 'top',
|
||||
backgroundColor = 'transparent'
|
||||
} = style
|
||||
|
||||
if (typeof lineHeight === 'string') { // 2em
|
||||
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize)
|
||||
}
|
||||
if (!text || (lineHeight > h)) return
|
||||
|
||||
ctx.save()
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.font = `${fontSize}px sans-serif`
|
||||
ctx.textAlign = textAlign
|
||||
|
||||
// 背景色
|
||||
ctx.fillStyle = backgroundColor
|
||||
this.roundRect(x, y, w, h, 0)
|
||||
|
||||
// 文字颜色
|
||||
ctx.fillStyle = color
|
||||
|
||||
// 水平布局
|
||||
switch (textAlign) {
|
||||
case 'left':
|
||||
break
|
||||
case 'center':
|
||||
x += 0.5 * w
|
||||
break
|
||||
case 'right':
|
||||
x += w
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
const textWidth = ctx.measureText(text).width
|
||||
const actualHeight = Math.ceil(textWidth / w) * lineHeight
|
||||
let paddingTop = Math.ceil((h - actualHeight) / 2)
|
||||
if (paddingTop < 0) paddingTop = 0
|
||||
|
||||
// 垂直布局
|
||||
switch (verticalAlign) {
|
||||
case 'top':
|
||||
break
|
||||
case 'middle':
|
||||
y += paddingTop
|
||||
break
|
||||
case 'bottom':
|
||||
y += 2 * paddingTop
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
|
||||
|
||||
// 不超过一行
|
||||
if (textWidth <= w) {
|
||||
ctx.fillText(text, x, y + inlinePaddingTop)
|
||||
return
|
||||
}
|
||||
|
||||
// 多行文本
|
||||
const chars = text.split('')
|
||||
const _y = y
|
||||
|
||||
// 逐行绘制
|
||||
let line = ''
|
||||
for (const ch of chars) {
|
||||
const testLine = line + ch
|
||||
const testWidth = ctx.measureText(testLine).width
|
||||
|
||||
if (testWidth > w) {
|
||||
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||
y += lineHeight
|
||||
line = ch
|
||||
if ((y + lineHeight) > (_y + h)) break
|
||||
} else {
|
||||
line = testLine
|
||||
}
|
||||
}
|
||||
|
||||
// 避免溢出
|
||||
if ((y + lineHeight) <= (_y + h)) {
|
||||
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||
}
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
async drawNode(element) {
|
||||
const {layoutBox, computedStyle, name} = element
|
||||
const {src, text} = element.attributes
|
||||
if (name === 'view') {
|
||||
this.drawView(layoutBox, computedStyle)
|
||||
} else if (name === 'image') {
|
||||
await this.drawImage(src, layoutBox, computedStyle)
|
||||
} else if (name === 'text') {
|
||||
this.drawText(text, layoutBox, computedStyle)
|
||||
}
|
||||
const childs = Object.values(element.children)
|
||||
for (const child of childs) {
|
||||
await this.drawNode(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
Draw
|
||||
}
|
||||
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
||||
});
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
<canvas wx:if="{{use2dCanvas}}" id="weui-canvas" type="2d" style="width: {{width}}px; height: {{height}}px;"></canvas>
|
||||
<canvas wx:else canvas-id="weui-canvas" style="width: {{width}}px; height: {{height}}px;"></canvas>
|
@ -1,57 +0,0 @@
|
||||
const hex = (color) => {
|
||||
let result = null
|
||||
|
||||
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||
return color
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||
return '#' + result[2].split(',').map((part, index) => {
|
||||
part = part.trim()
|
||||
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||
part = part.toString(16)
|
||||
if (part.length === 1) {
|
||||
part = '0' + part
|
||||
}
|
||||
return part
|
||||
}).join('')
|
||||
} else {
|
||||
return '#00000000'
|
||||
}
|
||||
}
|
||||
|
||||
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||
if (index === 0) {
|
||||
return part
|
||||
}
|
||||
return part[0].toUpperCase() + part.slice(1)
|
||||
}).join('')
|
||||
|
||||
const compareVersion = (v1, v2) => {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hex,
|
||||
splitLineToCamelCase,
|
||||
compareVersion
|
||||
}
|
5
node_modules/.store/node_modules/widget-ui/package.json
generated
vendored
5
node_modules/.store/node_modules/widget-ui/package.json
generated
vendored
@ -23,8 +23,5 @@
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.1",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"__npminstall_done": true,
|
||||
"_from": "widget-ui@1.0.2",
|
||||
"_resolved": "https://registry.npmmirror.com/widget-ui/-/widget-ui-1.0.2.tgz"
|
||||
}
|
||||
}
|
||||
|
5
node_modules/.store/widget-ui@1.0.2/node_modules/widget-ui/package.json
generated
vendored
5
node_modules/.store/widget-ui@1.0.2/node_modules/widget-ui/package.json
generated
vendored
@ -23,8 +23,5 @@
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.1",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"__npminstall_done": true,
|
||||
"_from": "widget-ui@1.0.2",
|
||||
"_resolved": "https://registry.npmmirror.com/widget-ui/-/widget-ui-1.0.2.tgz"
|
||||
}
|
||||
}
|
||||
|
5
node_modules/.store/wxml-to-canvas@1.1.1/node_modules/widget-ui/package.json
generated
vendored
5
node_modules/.store/wxml-to-canvas@1.1.1/node_modules/widget-ui/package.json
generated
vendored
@ -23,8 +23,5 @@
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.1",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"__npminstall_done": true,
|
||||
"_from": "widget-ui@1.0.2",
|
||||
"_resolved": "https://registry.npmmirror.com/widget-ui/-/widget-ui-1.0.2.tgz"
|
||||
}
|
||||
}
|
||||
|
5
node_modules/.store/wxml-to-canvas@1.1.1/node_modules/wxml-to-canvas/package.json
generated
vendored
5
node_modules/.store/wxml-to-canvas@1.1.1/node_modules/wxml-to-canvas/package.json
generated
vendored
@ -59,8 +59,5 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"widget-ui": "^1.0.2"
|
||||
},
|
||||
"__npminstall_done": true,
|
||||
"_from": "wxml-to-canvas@1.1.1",
|
||||
"_resolved": "https://registry.npmmirror.com/wxml-to-canvas/-/wxml-to-canvas-1.1.1.tgz"
|
||||
}
|
||||
}
|
||||
|
9
node_modules/widget-ui/babel.config.js
generated
vendored
9
node_modules/widget-ui/babel.config.js
generated
vendored
@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
["@babel/preset-env", {
|
||||
targets: {
|
||||
node: "current"
|
||||
}
|
||||
}]
|
||||
]
|
||||
};
|
40
node_modules/widget-ui/dist/element.d.ts
generated
vendored
40
node_modules/widget-ui/dist/element.d.ts
generated
vendored
@ -1,40 +0,0 @@
|
||||
declare type LayoutData = {
|
||||
left: number;
|
||||
top: number;
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
declare type LayoutNode = {
|
||||
id: number;
|
||||
style: Object;
|
||||
children: LayoutNode[];
|
||||
layout?: LayoutData;
|
||||
};
|
||||
declare class Element {
|
||||
static uuid(): number;
|
||||
parent: Element | null;
|
||||
id: number;
|
||||
style: {
|
||||
[key: string]: any;
|
||||
};
|
||||
computedStyle: {
|
||||
[key: string]: any;
|
||||
};
|
||||
lastComputedStyle: {
|
||||
[key: string]: any;
|
||||
};
|
||||
children: {
|
||||
[key: string]: Element;
|
||||
};
|
||||
layoutBox: LayoutData;
|
||||
constructor(style?: {
|
||||
[key: string]: any;
|
||||
});
|
||||
getAbsolutePosition(element: Element): any;
|
||||
add(element: Element): void;
|
||||
remove(element?: Element): void;
|
||||
getNodeTree(): LayoutNode;
|
||||
applyLayout(layoutNode: LayoutNode): void;
|
||||
layout(): void;
|
||||
}
|
||||
export default Element;
|
5
node_modules/widget-ui/dist/event.d.ts
generated
vendored
5
node_modules/widget-ui/dist/event.d.ts
generated
vendored
@ -1,5 +0,0 @@
|
||||
export default class EventEmitter {
|
||||
emit(event: string, data?: any): void;
|
||||
on(event: string, callback: any): void;
|
||||
off(event: string, callback: any): void;
|
||||
}
|
1
node_modules/widget-ui/dist/index.js
generated
vendored
1
node_modules/widget-ui/dist/index.js
generated
vendored
File diff suppressed because one or more lines are too long
36
node_modules/widget-ui/dist/style.d.ts
generated
vendored
36
node_modules/widget-ui/dist/style.d.ts
generated
vendored
@ -1,36 +0,0 @@
|
||||
declare const textStyles: string[];
|
||||
declare const scalableStyles: string[];
|
||||
declare const layoutAffectedStyles: string[];
|
||||
declare const getDefaultStyle: () => {
|
||||
left: undefined;
|
||||
top: undefined;
|
||||
right: undefined;
|
||||
bottom: undefined;
|
||||
width: undefined;
|
||||
height: undefined;
|
||||
maxWidth: undefined;
|
||||
maxHeight: undefined;
|
||||
minWidth: undefined;
|
||||
minHeight: undefined;
|
||||
margin: undefined;
|
||||
marginLeft: undefined;
|
||||
marginRight: undefined;
|
||||
marginTop: undefined;
|
||||
marginBottom: undefined;
|
||||
padding: undefined;
|
||||
paddingLeft: undefined;
|
||||
paddingRight: undefined;
|
||||
paddingTop: undefined;
|
||||
paddingBottom: undefined;
|
||||
borderWidth: undefined;
|
||||
flexDirection: undefined;
|
||||
justifyContent: undefined;
|
||||
alignItems: undefined;
|
||||
alignSelf: undefined;
|
||||
flex: undefined;
|
||||
flexWrap: undefined;
|
||||
position: undefined;
|
||||
hidden: boolean;
|
||||
scale: number;
|
||||
};
|
||||
export { getDefaultStyle, scalableStyles, textStyles, layoutAffectedStyles };
|
6
node_modules/widget-ui/jest.config.js
generated
vendored
6
node_modules/widget-ui/jest.config.js
generated
vendored
@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
transform: {
|
||||
"^.+\\.js$": "babel-jest",
|
||||
"^.+\\.ts$": "ts-jest"
|
||||
}
|
||||
};
|
30
node_modules/widget-ui/package.json
generated
vendored
30
node_modules/widget-ui/package.json
generated
vendored
@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "widget-ui",
|
||||
"version": "1.0.2",
|
||||
"description": "",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "webpack"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"eventemitter3": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.6.3",
|
||||
"@babel/preset-typescript": "^7.6.0",
|
||||
"@types/jest": "^24.0.18",
|
||||
"babel-jest": "^24.9.0",
|
||||
"jest": "^24.9.0",
|
||||
"ts-jest": "^24.1.0",
|
||||
"ts-loader": "^6.2.0",
|
||||
"typescript": "^3.6.4",
|
||||
"webpack": "^4.41.1",
|
||||
"webpack-cli": "^3.3.9"
|
||||
},
|
||||
"__npminstall_done": true,
|
||||
"_from": "widget-ui@1.0.2",
|
||||
"_resolved": "https://registry.npmmirror.com/widget-ui/-/widget-ui-1.0.2.tgz"
|
||||
}
|
1186
node_modules/widget-ui/src/css-layout.js
generated
vendored
1186
node_modules/widget-ui/src/css-layout.js
generated
vendored
File diff suppressed because it is too large
Load Diff
172
node_modules/widget-ui/src/element.ts
generated
vendored
172
node_modules/widget-ui/src/element.ts
generated
vendored
@ -1,172 +0,0 @@
|
||||
|
||||
import computeLayout from "./css-layout";
|
||||
import { getDefaultStyle, scalableStyles, layoutAffectedStyles } from "./style";
|
||||
|
||||
type LayoutData = {
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number
|
||||
};
|
||||
|
||||
type LayoutNode = {
|
||||
id: number,
|
||||
style: Object,
|
||||
children: LayoutNode[],
|
||||
layout?: LayoutData
|
||||
};
|
||||
|
||||
let uuid = 0;
|
||||
|
||||
class Element {
|
||||
public static uuid(): number {
|
||||
return uuid++;
|
||||
}
|
||||
|
||||
public parent: Element | null = null;
|
||||
public id: number = Element.uuid();
|
||||
public style: { [key: string]: any } = {};
|
||||
public computedStyle: { [key: string]: any } = {};
|
||||
public lastComputedStyle: { [key: string]: any } = {};
|
||||
public children: { [key: string]: Element } = {};
|
||||
public layoutBox: LayoutData = { left: 0, top: 0, width: 0, height: 0 };
|
||||
|
||||
constructor(style: { [key: string]: any } = {}) {
|
||||
// 拷贝一份,防止被外部逻辑修改
|
||||
style = Object.assign(getDefaultStyle(), style);
|
||||
this.computedStyle = Object.assign(getDefaultStyle(), style);
|
||||
this.lastComputedStyle = Object.assign(getDefaultStyle(), style);
|
||||
|
||||
Object.keys(style).forEach(key => {
|
||||
Object.defineProperty(this.style, key, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get: () => style[key],
|
||||
set: (value: any) => {
|
||||
if (value === style[key] || value === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.lastComputedStyle = this.computedStyle[key]
|
||||
style[key] = value
|
||||
this.computedStyle[key] = value
|
||||
|
||||
// 如果设置的是一个可缩放的属性, 计算自己
|
||||
if (scalableStyles.includes(key) && this.style.scale) {
|
||||
this.computedStyle[key] = value * this.style.scale
|
||||
}
|
||||
|
||||
// 如果设置的是 scale, 则把所有可缩放的属性计算
|
||||
if (key === "scale") {
|
||||
scalableStyles.forEach(prop => {
|
||||
if (style[prop]) {
|
||||
this.computedStyle[prop] = style[prop] * value
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (key === "hidden") {
|
||||
if (value) {
|
||||
layoutAffectedStyles.forEach((key: string) => {
|
||||
this.computedStyle[key] = 0;
|
||||
});
|
||||
} else {
|
||||
layoutAffectedStyles.forEach((key: string) => {
|
||||
this.computedStyle[key] = this.lastComputedStyle[key];
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (this.style.scale) {
|
||||
scalableStyles.forEach((key: string) => {
|
||||
if (this.style[key]) {
|
||||
const computedValue = this.style[key] * this.style.scale;
|
||||
this.computedStyle[key] = computedValue;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (style.hidden) {
|
||||
layoutAffectedStyles.forEach((key: string) => {
|
||||
this.computedStyle[key] = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getAbsolutePosition(element: Element) {
|
||||
if (!element) {
|
||||
return this.getAbsolutePosition(this)
|
||||
}
|
||||
|
||||
if (!element.parent) {
|
||||
return {
|
||||
left: 0,
|
||||
top: 0
|
||||
}
|
||||
}
|
||||
|
||||
const {left, top} = this.getAbsolutePosition(element.parent)
|
||||
|
||||
return {
|
||||
left: left + element.layoutBox.left,
|
||||
top: top + element.layoutBox.top
|
||||
}
|
||||
}
|
||||
|
||||
public add(element: Element) {
|
||||
element.parent = this;
|
||||
this.children[element.id] = element;
|
||||
}
|
||||
|
||||
public remove(element?: Element) {
|
||||
// 删除自己
|
||||
if (!element) {
|
||||
Object.keys(this.children).forEach(id => {
|
||||
const child = this.children[id]
|
||||
child.remove()
|
||||
delete this.children[id]
|
||||
})
|
||||
} else if (this.children[element.id]) {
|
||||
// 是自己的子节点才删除
|
||||
element.remove()
|
||||
delete this.children[element.id];
|
||||
}
|
||||
}
|
||||
|
||||
public getNodeTree(): LayoutNode {
|
||||
return {
|
||||
id: this.id,
|
||||
style: this.computedStyle,
|
||||
children: Object.keys(this.children).map((id: string) => {
|
||||
const child = this.children[id];
|
||||
return child.getNodeTree();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public applyLayout(layoutNode: LayoutNode) {
|
||||
["left", "top", "width", "height"].forEach((key: string) => {
|
||||
if (layoutNode.layout && typeof layoutNode.layout[key] === "number") {
|
||||
this.layoutBox[key] = layoutNode.layout[key];
|
||||
if (this.parent && (key === "left" || key === "top")) {
|
||||
this.layoutBox[key] += this.parent.layoutBox[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
layoutNode.children.forEach((child: LayoutNode) => {
|
||||
this.children[child.id].applyLayout(child);
|
||||
});
|
||||
}
|
||||
|
||||
layout() {
|
||||
const nodeTree = this.getNodeTree();
|
||||
computeLayout(nodeTree);
|
||||
this.applyLayout(nodeTree);
|
||||
}
|
||||
}
|
||||
|
||||
export default Element;
|
15
node_modules/widget-ui/src/event.ts
generated
vendored
15
node_modules/widget-ui/src/event.ts
generated
vendored
@ -1,15 +0,0 @@
|
||||
import _EventEmitter from "eventemitter3";
|
||||
const emitter = new _EventEmitter();
|
||||
export default class EventEmitter {
|
||||
public emit(event: string, data?: any) {
|
||||
emitter.emit(event, data);
|
||||
}
|
||||
|
||||
public on(event: string, callback) {
|
||||
emitter.on(event, callback);
|
||||
}
|
||||
|
||||
public off(event: string, callback) {
|
||||
emitter.off(event, callback);
|
||||
}
|
||||
}
|
87
node_modules/widget-ui/src/style.ts
generated
vendored
87
node_modules/widget-ui/src/style.ts
generated
vendored
@ -1,87 +0,0 @@
|
||||
const textStyles: string[] = ["color", "fontSize", "textAlign", "fontWeight", "lineHeight", "lineBreak"];
|
||||
|
||||
const scalableStyles: string[] = ["left", "top", "right", "bottom", "width", "height",
|
||||
"margin", "marginLeft", "marginRight", "marginTop", "marginBottom",
|
||||
"padding", "paddingLeft", "paddingRight", "paddingTop", "paddingBottom",
|
||||
"borderWidth", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth"];
|
||||
|
||||
const layoutAffectedStyles: string[] = [
|
||||
"margin", "marginTop", "marginBottom", "marginLeft", "marginRight",
|
||||
"padding", "paddingTop", "paddingBottom", "paddingLeft", "paddingRight",
|
||||
"width", "height"];
|
||||
|
||||
type Style = {
|
||||
left: number,
|
||||
top: number,
|
||||
right: number,
|
||||
bottom: number,
|
||||
width: number,
|
||||
height: number,
|
||||
maxWidth: number,
|
||||
maxHeight: number,
|
||||
minWidth: number,
|
||||
minHeight: number,
|
||||
margin: number,
|
||||
marginLeft: number,
|
||||
marginRight: number,
|
||||
marginTop: number,
|
||||
marginBottom: number,
|
||||
padding: number,
|
||||
paddingLeft: number,
|
||||
paddingRight: number,
|
||||
paddingTop: number,
|
||||
paddingBottom: number,
|
||||
borderWidth: number,
|
||||
borderLeftWidth: number,
|
||||
borderRightWidth: number,
|
||||
borderTopWidth: number,
|
||||
borderBottomWidth: number,
|
||||
flexDirection: "column" | "row",
|
||||
justifyContent: "flex-start" | "center" | "flex-end" | "space-between" | "space-around",
|
||||
alignItems: "flex-start" | "center" | "flex-end" | "stretch",
|
||||
alignSelf: "flex-start" | "center" | "flex-end" | "stretch",
|
||||
flex: number,
|
||||
flexWrap: "wrap" | "nowrap",
|
||||
position: "relative" | "absolute",
|
||||
|
||||
hidden: boolean,
|
||||
scale: number
|
||||
}
|
||||
|
||||
const getDefaultStyle = () => ({
|
||||
left: undefined,
|
||||
top: undefined,
|
||||
right: undefined,
|
||||
bottom: undefined,
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
maxWidth: undefined,
|
||||
maxHeight: undefined,
|
||||
minWidth: undefined,
|
||||
minHeight: undefined,
|
||||
margin: undefined,
|
||||
marginLeft: undefined,
|
||||
marginRight: undefined,
|
||||
marginTop: undefined,
|
||||
marginBottom: undefined,
|
||||
padding: undefined,
|
||||
paddingLeft: undefined,
|
||||
paddingRight: undefined,
|
||||
paddingTop: undefined,
|
||||
paddingBottom: undefined,
|
||||
borderWidth: undefined,
|
||||
flexDirection: undefined,
|
||||
justifyContent: undefined,
|
||||
alignItems: undefined,
|
||||
alignSelf: undefined,
|
||||
flex: undefined,
|
||||
flexWrap: undefined,
|
||||
position: undefined,
|
||||
|
||||
hidden: false,
|
||||
scale: 1
|
||||
})
|
||||
|
||||
export {
|
||||
getDefaultStyle, scalableStyles, textStyles, layoutAffectedStyles
|
||||
}
|
183
node_modules/widget-ui/test/css-layout.test.ts
generated
vendored
183
node_modules/widget-ui/test/css-layout.test.ts
generated
vendored
@ -1,183 +0,0 @@
|
||||
|
||||
import Element from "../src/element";
|
||||
|
||||
test("layout", () => {
|
||||
const container = new Element({
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 10,
|
||||
borderWidth: 2
|
||||
})
|
||||
|
||||
const div1 = new Element({
|
||||
left: 5,
|
||||
top: 5,
|
||||
width: 14,
|
||||
height: 14
|
||||
})
|
||||
|
||||
container.add(div1);
|
||||
container.layout();
|
||||
// css-layout 是 border-box
|
||||
expect(container.layoutBox.left).toBe(0);
|
||||
expect(container.layoutBox.top).toBe(0);
|
||||
expect(container.layoutBox.width).toBe(100);
|
||||
expect(container.layoutBox.height).toBe(100);
|
||||
|
||||
expect(div1.layoutBox.left).toBe(10 + 2 + 5);
|
||||
expect(div1.layoutBox.top).toBe(10 + 2 + 5);
|
||||
expect(div1.layoutBox.width).toBe(14);
|
||||
expect(div1.layoutBox.height).toBe(14);
|
||||
});
|
||||
|
||||
test("overflow", () => {
|
||||
const container = new Element({
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 10,
|
||||
borderWidth: 2
|
||||
})
|
||||
|
||||
const div1 = new Element({
|
||||
width: 114,
|
||||
height: 114,
|
||||
})
|
||||
|
||||
container.add(div1);
|
||||
container.layout();
|
||||
|
||||
// 写死尺寸的情况下子元素不收缩父元素不撑开
|
||||
expect(container.layoutBox.width).toBe(100);
|
||||
expect(container.layoutBox.height).toBe(100);
|
||||
|
||||
expect(div1.layoutBox.left).toBe(10 + 2);
|
||||
expect(div1.layoutBox.top).toBe(10 + 2);
|
||||
expect(div1.layoutBox.width).toBe(114);
|
||||
expect(div1.layoutBox.height).toBe(114);
|
||||
});
|
||||
|
||||
test("right bottom", () => {
|
||||
const container = new Element({
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 10,
|
||||
borderWidth: 2
|
||||
})
|
||||
|
||||
const div1 = new Element({
|
||||
width: 14,
|
||||
height: 14,
|
||||
right: 13,
|
||||
bottom: 9,
|
||||
position: "absolute"
|
||||
})
|
||||
|
||||
container.add(div1);
|
||||
container.layout();
|
||||
|
||||
// right bottom 只有在 position 为 absolute 的情况下才有用
|
||||
expect(container.layoutBox.width).toBe(100);
|
||||
expect(container.layoutBox.height).toBe(100);
|
||||
|
||||
// 但这时就是以整个父元素为边界,而不是 border + padding 后的边界
|
||||
expect(div1.layoutBox.left).toBe(100 - 13 - 14);
|
||||
expect(div1.layoutBox.top).toBe(100 - 9 - 14);
|
||||
});
|
||||
|
||||
test("flex center", () => {
|
||||
const container = new Element({
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 10,
|
||||
borderWidth: 2,
|
||||
flexDirection: "row",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
})
|
||||
|
||||
const div1 = new Element({
|
||||
width: 14,
|
||||
height: 14
|
||||
})
|
||||
|
||||
container.add(div1);
|
||||
container.layout();
|
||||
// 使用 flex 水平垂直居中
|
||||
expect(div1.layoutBox.left).toBe((100 - 14)/2);
|
||||
expect(div1.layoutBox.top).toBe((100 - 14)/2);
|
||||
})
|
||||
|
||||
test("flex top bottom", () => {
|
||||
const container = new Element({
|
||||
width: 100,
|
||||
height: 100,
|
||||
padding: 10,
|
||||
borderWidth: 2,
|
||||
flexDirection: "column",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "stretch"
|
||||
})
|
||||
|
||||
// flex 实现一上一下两行水平填满
|
||||
const div1 = new Element({
|
||||
height: 10
|
||||
})
|
||||
|
||||
const div2 = new Element({
|
||||
height: 20
|
||||
})
|
||||
|
||||
container.add(div1);
|
||||
container.add(div2);
|
||||
container.layout();
|
||||
|
||||
expect(div1.layoutBox.left).toBe(10 + 2);
|
||||
expect(div1.layoutBox.top).toBe(10 + 2);
|
||||
expect(div1.layoutBox.width).toBe(100 - 10*2 - 2*2);
|
||||
|
||||
expect(div2.layoutBox.left).toBe(10 + 2);
|
||||
expect(div2.layoutBox.top).toBe(100 - 10 - 2 - 20);
|
||||
expect(div2.layoutBox.width).toBe(100 - 10*2 - 2*2);
|
||||
})
|
||||
|
||||
test("rewrite uuid", () => {
|
||||
// 小程序为了保证 webview 和 service 侧的 coverview 不冲突,所以设置了不同的自增起点
|
||||
// uuid 静态方法就是为了根据不同的需求去覆写
|
||||
let uuid = 79648527;
|
||||
Element.uuid = () => uuid++;
|
||||
const container = new Element();
|
||||
expect(container.id).toEqual(79648527);
|
||||
const div = new Element();
|
||||
expect(div.id).toEqual(79648528);
|
||||
});
|
||||
|
||||
test("absolute left top", () => {
|
||||
const container = new Element({
|
||||
width: 300,
|
||||
height: 200,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center'
|
||||
})
|
||||
|
||||
|
||||
const div1 = new Element({
|
||||
width: 80,
|
||||
height: 60
|
||||
})
|
||||
|
||||
const div2 = new Element({
|
||||
width: 40,
|
||||
height: 30
|
||||
})
|
||||
|
||||
div1.add(div2)
|
||||
container.add(div1)
|
||||
container.layout()
|
||||
|
||||
expect(div1.layoutBox.left).toBe(110)
|
||||
expect(div1.layoutBox.top).toBe(70)
|
||||
|
||||
expect(div2.layoutBox.left).toBe(110)
|
||||
expect(div2.layoutBox.top).toBe(70)
|
||||
})
|
47
node_modules/widget-ui/tsconfig.json
generated
vendored
47
node_modules/widget-ui/tsconfig.json
generated
vendored
@ -1,47 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"resolveJsonModule": true,
|
||||
"downlevelIteration": false,
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es5",
|
||||
"es2015.promise",
|
||||
"es2016",
|
||||
"dom"
|
||||
],
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"*"
|
||||
],
|
||||
"*": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"typeRoots": [
|
||||
"./node_modules/@types"
|
||||
],
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"stripInternal": true,
|
||||
"experimentalDecorators": true,
|
||||
"noImplicitReturns": true,
|
||||
"alwaysStrict": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"removeComments": false,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"skipLibCheck": true,
|
||||
"pretty": true,
|
||||
"strictPropertyInitialization": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
206
node_modules/widget-ui/tslint.json
generated
vendored
206
node_modules/widget-ui/tslint.json
generated
vendored
@ -1,206 +0,0 @@
|
||||
{
|
||||
"defaultSeverity": "error",
|
||||
"extends": [],
|
||||
"rules": {
|
||||
"adjacent-overload-signatures": true,
|
||||
"align": {
|
||||
"options": [
|
||||
"parameters",
|
||||
"statements"
|
||||
]
|
||||
},
|
||||
"arrow-return-shorthand": true,
|
||||
"ban-types": {
|
||||
"options": [
|
||||
[
|
||||
"Object",
|
||||
"Avoid using the `Object` type. Did you mean `object`?"
|
||||
],
|
||||
[
|
||||
"Function",
|
||||
"Avoid using the `Function` type. Prefer a specific function type, like `() => void`."
|
||||
],
|
||||
[
|
||||
"Boolean",
|
||||
"Avoid using the `Boolean` type. Did you mean `boolean`?"
|
||||
],
|
||||
[
|
||||
"Number",
|
||||
"Avoid using the `Number` type. Did you mean `number`?"
|
||||
],
|
||||
[
|
||||
"String",
|
||||
"Avoid using the `String` type. Did you mean `string`?"
|
||||
],
|
||||
[
|
||||
"Symbol",
|
||||
"Avoid using the `Symbol` type. Did you mean `symbol`?"
|
||||
]
|
||||
]
|
||||
},
|
||||
"comment-format": {
|
||||
"options": [
|
||||
"check-space"
|
||||
]
|
||||
},
|
||||
"curly": {
|
||||
"options": [
|
||||
"ignore-same-line"
|
||||
]
|
||||
},
|
||||
"cyclomatic-complexity": false,
|
||||
"import-spacing": true,
|
||||
"indent": {
|
||||
"options": [
|
||||
"spaces"
|
||||
]
|
||||
},
|
||||
"interface-over-type-literal": true,
|
||||
"member-ordering": [
|
||||
true,
|
||||
{
|
||||
"order": [
|
||||
"public-static-field",
|
||||
"public-instance-field",
|
||||
"private-static-field",
|
||||
"private-instance-field",
|
||||
"public-constructor",
|
||||
"private-constructor",
|
||||
"public-instance-method",
|
||||
"protected-instance-method",
|
||||
"private-instance-method"
|
||||
],
|
||||
"alphabetize": false
|
||||
}
|
||||
],
|
||||
"no-angle-bracket-type-assertion": true,
|
||||
"no-arg": true,
|
||||
"no-conditional-assignment": true,
|
||||
"no-debugger": true,
|
||||
"no-duplicate-super": true,
|
||||
"no-eval": true,
|
||||
"no-internal-module": true,
|
||||
"no-misused-new": true,
|
||||
"no-reference-import": true,
|
||||
"no-string-literal": true,
|
||||
"no-string-throw": true,
|
||||
"no-unnecessary-initializer": true,
|
||||
"no-unsafe-finally": true,
|
||||
"no-unused-expression": true,
|
||||
"no-use-before-declare": false,
|
||||
"no-var-keyword": true,
|
||||
"no-var-requires": true,
|
||||
"one-line": {
|
||||
"options": [
|
||||
"check-catch",
|
||||
"check-else",
|
||||
"check-finally",
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
]
|
||||
},
|
||||
"one-variable-per-declaration": {
|
||||
"options": [
|
||||
"ignore-for-loop"
|
||||
]
|
||||
},
|
||||
"ordered-imports": {
|
||||
"options": {
|
||||
"import-sources-order": "case-insensitive",
|
||||
"module-source-path": "full",
|
||||
"named-imports-order": "case-insensitive"
|
||||
}
|
||||
},
|
||||
"prefer-const": true,
|
||||
"prefer-for-of": false,
|
||||
"quotemark": {
|
||||
"options": [
|
||||
"double",
|
||||
"avoid-escape"
|
||||
]
|
||||
},
|
||||
"radix": true,
|
||||
"semicolon": {
|
||||
"options": [
|
||||
"always"
|
||||
]
|
||||
},
|
||||
"space-before-function-paren": {
|
||||
"options": {
|
||||
"anonymous": "never",
|
||||
"asyncArrow": "always",
|
||||
"constructor": "never",
|
||||
"method": "never",
|
||||
"named": "never"
|
||||
}
|
||||
},
|
||||
"trailing-comma": {
|
||||
"options": {
|
||||
"esSpecCompliant": true,
|
||||
"multiline": {
|
||||
"objects": "always",
|
||||
"arrays": "always",
|
||||
"functions": "always",
|
||||
"typeLiterals": "always"
|
||||
},
|
||||
"singleline": "never"
|
||||
}
|
||||
},
|
||||
"triple-equals": {
|
||||
"options": [
|
||||
"allow-null-check"
|
||||
]
|
||||
},
|
||||
"typedef": false,
|
||||
"typedef-whitespace": {
|
||||
"options": [
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
},
|
||||
{
|
||||
"call-signature": "onespace",
|
||||
"index-signature": "onespace",
|
||||
"parameter": "onespace",
|
||||
"property-declaration": "onespace",
|
||||
"variable-declaration": "onespace"
|
||||
}
|
||||
]
|
||||
},
|
||||
"typeof-compare": false,
|
||||
"unified-signatures": true,
|
||||
"use-isnan": true,
|
||||
"whitespace": {
|
||||
"options": [
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type",
|
||||
"check-typecast"
|
||||
]
|
||||
}
|
||||
},
|
||||
"jsRules": {},
|
||||
"rulesDirectory": [],
|
||||
"no-var-requires": false,
|
||||
"trailing-comma": [
|
||||
true,
|
||||
{
|
||||
"multiline": {
|
||||
"objects": "always",
|
||||
"arrays": "always",
|
||||
"functions": "always",
|
||||
"typeLiterals": "ignore"
|
||||
},
|
||||
"esSpecCompliant": true
|
||||
}
|
||||
],
|
||||
"no-unused-expression": [
|
||||
true,
|
||||
"allow-fast-null-checks"
|
||||
]
|
||||
}
|
25
node_modules/widget-ui/webpack.config.js
generated
vendored
25
node_modules/widget-ui/webpack.config.js
generated
vendored
@ -1,25 +0,0 @@
|
||||
const path = require("path");
|
||||
|
||||
module.exports = {
|
||||
mode: "production",
|
||||
entry: path.resolve(__dirname, "src/element.ts"),
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: "ts-loader",
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: [".js", ".ts"]
|
||||
},
|
||||
output: {
|
||||
filename: "index.js",
|
||||
path: path.resolve(__dirname, "dist"),
|
||||
libraryTarget: "umd", // 采用通用模块定义
|
||||
libraryExport: "default", // 兼容 ES6(ES2015) 的模块系统、CommonJS 和 AMD 模块规范
|
||||
globalObject: "this" // 兼容node和浏览器运行,避免window is not undefined情况
|
||||
}
|
||||
};
|
10
node_modules/wxml-to-canvas/.babelrc
generated
vendored
10
node_modules/wxml-to-canvas/.babelrc
generated
vendored
@ -1,10 +0,0 @@
|
||||
{
|
||||
"plugins": [
|
||||
["module-resolver", {
|
||||
"root": ["./src"],
|
||||
"alias": {}
|
||||
}],
|
||||
"@babel/transform-runtime"
|
||||
],
|
||||
"presets": ["@babel/preset-env"]
|
||||
}
|
99
node_modules/wxml-to-canvas/.eslintrc.js
generated
vendored
99
node_modules/wxml-to-canvas/.eslintrc.js
generated
vendored
@ -1,99 +0,0 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'airbnb-base',
|
||||
'plugin:promise/recommended'
|
||||
],
|
||||
'parserOptions': {
|
||||
'ecmaVersion': 9,
|
||||
'ecmaFeatures': {
|
||||
'jsx': false
|
||||
},
|
||||
'sourceType': 'module'
|
||||
},
|
||||
'env': {
|
||||
'es6': true,
|
||||
'node': true,
|
||||
'jest': true
|
||||
},
|
||||
'plugins': [
|
||||
'import',
|
||||
'node',
|
||||
'promise'
|
||||
],
|
||||
'rules': {
|
||||
'arrow-parens': 'off',
|
||||
'comma-dangle': [
|
||||
'error',
|
||||
'only-multiline'
|
||||
],
|
||||
'complexity': ['error', 10],
|
||||
'func-names': 'off',
|
||||
'global-require': 'off',
|
||||
'handle-callback-err': [
|
||||
'error',
|
||||
'^(err|error)$'
|
||||
],
|
||||
'import/no-unresolved': [
|
||||
'error',
|
||||
{
|
||||
'caseSensitive': true,
|
||||
'commonjs': true,
|
||||
'ignore': ['^[^.]']
|
||||
}
|
||||
],
|
||||
'import/prefer-default-export': 'off',
|
||||
'linebreak-style': 'off',
|
||||
'no-catch-shadow': 'error',
|
||||
'no-continue': 'off',
|
||||
'no-div-regex': 'warn',
|
||||
'no-else-return': 'off',
|
||||
'no-param-reassign': 'off',
|
||||
'no-plusplus': 'off',
|
||||
'no-shadow': 'off',
|
||||
'no-multi-assign': 'off',
|
||||
'no-underscore-dangle': 'off',
|
||||
'node/no-deprecated-api': 'error',
|
||||
'node/process-exit-as-throw': 'error',
|
||||
'object-curly-spacing': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'operator-linebreak': [
|
||||
'error',
|
||||
'after',
|
||||
{
|
||||
'overrides': {
|
||||
':': 'before',
|
||||
'?': 'before'
|
||||
}
|
||||
}
|
||||
],
|
||||
'prefer-arrow-callback': 'off',
|
||||
'prefer-destructuring': 'off',
|
||||
'prefer-template': 'off',
|
||||
'quote-props': [
|
||||
1,
|
||||
'as-needed',
|
||||
{
|
||||
'unnecessary': true
|
||||
}
|
||||
],
|
||||
'semi': [
|
||||
'error',
|
||||
'never'
|
||||
],
|
||||
'no-await-in-loop': 'off',
|
||||
'no-restricted-syntax': 'off',
|
||||
'promise/always-return': 'off',
|
||||
},
|
||||
'globals': {
|
||||
'window': true,
|
||||
'document': true,
|
||||
'App': true,
|
||||
'Page': true,
|
||||
'Component': true,
|
||||
'Behavior': true,
|
||||
'wx': true,
|
||||
'getCurrentPages': true,
|
||||
}
|
||||
}
|
21
node_modules/wxml-to-canvas/LICENSE
generated
vendored
21
node_modules/wxml-to-canvas/LICENSE
generated
vendored
@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 wechat-miniprogram
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
187
node_modules/wxml-to-canvas/README.md
generated
vendored
187
node_modules/wxml-to-canvas/README.md
generated
vendored
@ -1,187 +0,0 @@
|
||||
# wxml-to-canvas
|
||||
|
||||
[](https://www.npmjs.com/package/wxml-to-canvas)
|
||||
[](https://github.com/wechat-miniprogram/wxml-to-canvas)
|
||||
|
||||
小程序内通过静态模板和样式绘制 canvas ,导出图片,可用于生成分享图等场景。[代码片段](https://developers.weixin.qq.com/s/r6UBlEm17pc6)
|
||||
|
||||
|
||||
## 使用方法
|
||||
|
||||
#### Step1. npm 安装,参考 [小程序 npm 支持](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)
|
||||
|
||||
```
|
||||
npm install --save wxml-to-canvas
|
||||
```
|
||||
|
||||
#### Step2. JSON 组件声明
|
||||
|
||||
```
|
||||
{
|
||||
"usingComponents": {
|
||||
"wxml-to-canvas": "wxml-to-canvas",
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Step3. wxml 引入组件
|
||||
|
||||
```
|
||||
<video class="video" src="{{src}}">
|
||||
<wxml-to-canvas class="widget"></wxml-to-canvas>
|
||||
</video>
|
||||
<image src="{{src}}" style="width: {{width}}px; height: {{height}}px"></image>
|
||||
```
|
||||
|
||||
##### 属性列表
|
||||
|
||||
| 属性 | 类型 | 默认值 | 必填 | 说明 |
|
||||
| --------------- | ------- | ------- | ---- | ---------------------- |
|
||||
| width | Number | 400 | 否 | 画布宽度 |
|
||||
| height | Number | 300 | 否 | 画布高度 |
|
||||
|
||||
|
||||
#### Step4. js 获取实例
|
||||
|
||||
```
|
||||
const {wxml, style} = require('./demo.js')
|
||||
Page({
|
||||
data: {
|
||||
src: ''
|
||||
},
|
||||
onLoad() {
|
||||
this.widget = this.selectComponent('.widget')
|
||||
},
|
||||
renderToCanvas() {
|
||||
const p1 = this.widget.renderToCanvas({ wxml, style })
|
||||
p1.then((res) => {
|
||||
this.container = res
|
||||
this.extraImage()
|
||||
})
|
||||
},
|
||||
extraImage() {
|
||||
const p2 = this.widget.canvasToTempFilePath()
|
||||
p2.then(res => {
|
||||
this.setData({
|
||||
src: res.tempFilePath,
|
||||
width: this.container.layoutBox.width,
|
||||
height: this.container.layoutBox.height
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
## wxml 模板
|
||||
|
||||
支持 `view`、`text`、`image` 三种标签,通过 class 匹配 style 对象中的样式。
|
||||
|
||||
```
|
||||
<view class="container" >
|
||||
<view class="item-box red">
|
||||
</view>
|
||||
<view class="item-box green" >
|
||||
<text class="text">yeah!</text>
|
||||
</view>
|
||||
<view class="item-box blue">
|
||||
<image class="img" src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=3582589792,4046843010&fm=26&gp=0.jpg"></image>
|
||||
</view>
|
||||
</view>
|
||||
```
|
||||
|
||||
## 样式
|
||||
|
||||
对象属性值为对应 wxml 标签的 cass 驼峰形式。**需为每个元素指定 width 和 height 属性**,否则会导致布局错误。
|
||||
|
||||
存在多个 className 时,位置靠后的优先级更高,子元素会继承父级元素的可继承属性。
|
||||
|
||||
元素均为 flex 布局。left/top 等 仅在 absolute 定位下生效。
|
||||
|
||||
```
|
||||
const style = {
|
||||
container: {
|
||||
width: 300,
|
||||
height: 200,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-around',
|
||||
backgroundColor: '#ccc',
|
||||
alignItems: 'center',
|
||||
},
|
||||
itemBox: {
|
||||
width: 80,
|
||||
height: 60,
|
||||
},
|
||||
red: {
|
||||
backgroundColor: '#ff0000'
|
||||
},
|
||||
green: {
|
||||
backgroundColor: '#00ff00'
|
||||
},
|
||||
blue: {
|
||||
backgroundColor: '#0000ff'
|
||||
},
|
||||
text: {
|
||||
width: 80,
|
||||
height: 60,
|
||||
textAlign: 'center',
|
||||
verticalAlign: 'middle',
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 接口
|
||||
|
||||
#### f1. `renderToCanvas({wxml, style}): Promise`
|
||||
|
||||
渲染到 canvas,传入 wxml 模板 和 style 对象,返回的容器对象包含布局和样式信息。
|
||||
|
||||
#### f2. `canvasToTempFilePath({fileType, quality}): Promise`
|
||||
|
||||
提取画布中容器所在区域内容生成相同大小的图片,返回临时文件地址。
|
||||
|
||||
`fileType` 支持 `jpg`、`png` 两种格式,quality 为图片的质量,目前仅对 jpg 有效。取值范围为 (0, 1],不在范围内时当作 1.0 处理。
|
||||
|
||||
## 支持的 css 属性
|
||||
|
||||
### 布局相关
|
||||
|
||||
| 属性名 | 支持的值或类型 | 默认值 |
|
||||
| --------------------- | --------------------------------------------------------- | ---------- |
|
||||
| width | number | 0 |
|
||||
| height | number | 0 |
|
||||
| position | relative, absolute | relative |
|
||||
| left | number | 0 |
|
||||
| top | number | 0 |
|
||||
| right | number | 0 |
|
||||
| bottom | number | 0 |
|
||||
| margin | number | 0 |
|
||||
| padding | number | 0 |
|
||||
| borderWidth | number | 0 |
|
||||
| borderRadius | number | 0 |
|
||||
| flexDirection | column, row | row |
|
||||
| flexShrink | number | 1 |
|
||||
| flexGrow | number | |
|
||||
| flexWrap | wrap, nowrap | nowrap |
|
||||
| justifyContent | flex-start, center, flex-end, space-between, space-around | flex-start |
|
||||
| alignItems, alignSelf | flex-start, center, flex-end, stretch | flex-start |
|
||||
|
||||
支持 marginLeft、paddingLeft 等
|
||||
|
||||
### 文字
|
||||
|
||||
| 属性名 | 支持的值或类型 | 默认值 |
|
||||
| --------------- | ------------------- | ----------- |
|
||||
| fontSize | number | 14 |
|
||||
| lineHeight | number / string | '1.4em' |
|
||||
| textAlign | left, center, right | left |
|
||||
| verticalAlign | top, middle, bottom | top |
|
||||
| color | string | #000000 |
|
||||
| backgroundColor | string | transparent |
|
||||
|
||||
lineHeight 可取带 em 单位的字符串或数字类型。
|
||||
|
||||
### 变形
|
||||
|
||||
| 属性名 | 支持的值或类型 | 默认值 |
|
||||
| ------ | -------------- | ------ |
|
||||
| scale | number | 1 |
|
26
node_modules/wxml-to-canvas/gulpfile.js
generated
vendored
26
node_modules/wxml-to-canvas/gulpfile.js
generated
vendored
@ -1,26 +0,0 @@
|
||||
const gulp = require('gulp')
|
||||
const clean = require('gulp-clean')
|
||||
|
||||
const config = require('./tools/config')
|
||||
const BuildTask = require('./tools/build')
|
||||
const id = require('./package.json').name || 'miniprogram-custom-component'
|
||||
|
||||
// 构建任务实例
|
||||
// eslint-disable-next-line no-new
|
||||
new BuildTask(id, config.entry)
|
||||
|
||||
// 清空生成目录和文件
|
||||
gulp.task('clean', gulp.series(() => gulp.src(config.distPath, {read: false, allowEmpty: true}).pipe(clean()), done => {
|
||||
if (config.isDev) {
|
||||
return gulp.src(config.demoDist, {read: false, allowEmpty: true})
|
||||
.pipe(clean())
|
||||
}
|
||||
|
||||
return done()
|
||||
}))
|
||||
// 监听文件变化并进行开发模式构建
|
||||
gulp.task('watch', gulp.series(`${id}-watch`))
|
||||
// 开发模式构建
|
||||
gulp.task('dev', gulp.series(`${id}-dev`))
|
||||
// 生产模式构建
|
||||
gulp.task('default', gulp.series(`${id}-default`))
|
779
node_modules/wxml-to-canvas/miniprogram_dist/index.js
generated
vendored
779
node_modules/wxml-to-canvas/miniprogram_dist/index.js
generated
vendored
@ -1,779 +0,0 @@
|
||||
(function webpackUniversalModuleDefinition(root, factory) {
|
||||
if(typeof exports === 'object' && typeof module === 'object')
|
||||
module.exports = factory();
|
||||
else if(typeof define === 'function' && define.amd)
|
||||
define([], factory);
|
||||
else {
|
||||
var a = factory();
|
||||
for(var i in a) (typeof exports === 'object' ? exports : root)[i] = a[i];
|
||||
}
|
||||
})(window, function() {
|
||||
return /******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = 1);
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([
|
||||
/* 0 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
const hex = (color) => {
|
||||
let result = null
|
||||
|
||||
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||
return color
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||
return '#' + result[2].split(',').map((part, index) => {
|
||||
part = part.trim()
|
||||
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||
part = part.toString(16)
|
||||
if (part.length === 1) {
|
||||
part = '0' + part
|
||||
}
|
||||
return part
|
||||
}).join('')
|
||||
} else {
|
||||
return '#00000000'
|
||||
}
|
||||
}
|
||||
|
||||
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||
if (index === 0) {
|
||||
return part
|
||||
}
|
||||
return part[0].toUpperCase() + part.slice(1)
|
||||
}).join('')
|
||||
|
||||
const compareVersion = (v1, v2) => {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hex,
|
||||
splitLineToCamelCase,
|
||||
compareVersion
|
||||
}
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 1 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
|
||||
const xmlParse = __webpack_require__(2)
|
||||
const {Widget} = __webpack_require__(3)
|
||||
const {Draw} = __webpack_require__(5)
|
||||
const {compareVersion} = __webpack_require__(0)
|
||||
|
||||
const canvasId = 'weui-canvas'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
width: {
|
||||
type: Number,
|
||||
value: 400
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
value: 300
|
||||
}
|
||||
},
|
||||
data: {
|
||||
use2dCanvas: false, // 2.9.2 后可用canvas 2d 接口
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const {SDKVersion, pixelRatio: dpr} = wx.getSystemInfoSync()
|
||||
const use2dCanvas = compareVersion(SDKVersion, '2.9.2') >= 0
|
||||
this.dpr = dpr
|
||||
this.setData({use2dCanvas}, () => {
|
||||
if (use2dCanvas) {
|
||||
const query = this.createSelectorQuery()
|
||||
query.select(`#${canvasId}`)
|
||||
.fields({node: true, size: true})
|
||||
.exec(res => {
|
||||
const canvas = res[0].node
|
||||
const ctx = canvas.getContext('2d')
|
||||
canvas.width = res[0].width * dpr
|
||||
canvas.height = res[0].height * dpr
|
||||
ctx.scale(dpr, dpr)
|
||||
this.ctx = ctx
|
||||
this.canvas = canvas
|
||||
})
|
||||
} else {
|
||||
this.ctx = wx.createCanvasContext(canvasId, this)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async renderToCanvas(args) {
|
||||
const {wxml, style} = args
|
||||
const ctx = this.ctx
|
||||
const canvas = this.canvas
|
||||
const use2dCanvas = this.data.use2dCanvas
|
||||
|
||||
if (use2dCanvas && !canvas) {
|
||||
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'))
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, this.data.width, this.data.height)
|
||||
const {root: xom} = xmlParse(wxml)
|
||||
|
||||
const widget = new Widget(xom, style)
|
||||
const container = widget.init()
|
||||
this.boundary = {
|
||||
top: container.layoutBox.top,
|
||||
left: container.layoutBox.left,
|
||||
width: container.computedStyle.width,
|
||||
height: container.computedStyle.height,
|
||||
}
|
||||
const draw = new Draw(ctx, canvas, use2dCanvas)
|
||||
await draw.drawNode(container)
|
||||
|
||||
if (!use2dCanvas) {
|
||||
await this.canvasDraw(ctx)
|
||||
}
|
||||
return Promise.resolve(container)
|
||||
},
|
||||
|
||||
canvasDraw(ctx, reserve) {
|
||||
return new Promise(resolve => {
|
||||
ctx.draw(reserve, () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
canvasToTempFilePath(args = {}) {
|
||||
const use2dCanvas = this.data.use2dCanvas
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const {
|
||||
top, left, width, height
|
||||
} = this.boundary
|
||||
|
||||
const copyArgs = {
|
||||
x: left,
|
||||
y: top,
|
||||
width,
|
||||
height,
|
||||
destWidth: width * this.dpr,
|
||||
destHeight: height * this.dpr,
|
||||
canvasId,
|
||||
fileType: args.fileType || 'png',
|
||||
quality: args.quality || 1,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
}
|
||||
|
||||
if (use2dCanvas) {
|
||||
delete copyArgs.canvasId
|
||||
copyArgs.canvas = this.canvas
|
||||
}
|
||||
wx.canvasToTempFilePath(copyArgs, this)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 2 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Expose `parse`.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given string of `xml`.
|
||||
*
|
||||
* @param {String} xml
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function parse(xml) {
|
||||
xml = xml.trim()
|
||||
|
||||
// strip comments
|
||||
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
||||
|
||||
return document()
|
||||
|
||||
/**
|
||||
* XML document.
|
||||
*/
|
||||
|
||||
function document() {
|
||||
return {
|
||||
declaration: declaration(),
|
||||
root: tag()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declaration.
|
||||
*/
|
||||
|
||||
function declaration() {
|
||||
const m = match(/^<\?xml\s*/)
|
||||
if (!m) return
|
||||
|
||||
// tag
|
||||
const node = {
|
||||
attributes: {}
|
||||
}
|
||||
|
||||
// attributes
|
||||
while (!(eos() || is('?>'))) {
|
||||
const attr = attribute()
|
||||
if (!attr) return node
|
||||
node.attributes[attr.name] = attr.value
|
||||
}
|
||||
|
||||
match(/\?>\s*/)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag.
|
||||
*/
|
||||
|
||||
function tag() {
|
||||
const m = match(/^<([\w-:.]+)\s*/)
|
||||
if (!m) return
|
||||
|
||||
// name
|
||||
const node = {
|
||||
name: m[1],
|
||||
attributes: {},
|
||||
children: []
|
||||
}
|
||||
|
||||
// attributes
|
||||
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
||||
const attr = attribute()
|
||||
if (!attr) return node
|
||||
node.attributes[attr.name] = attr.value
|
||||
}
|
||||
|
||||
// self closing tag
|
||||
if (match(/^\s*\/>\s*/)) {
|
||||
return node
|
||||
}
|
||||
|
||||
match(/\??>\s*/)
|
||||
|
||||
// content
|
||||
node.content = content()
|
||||
|
||||
// children
|
||||
let child
|
||||
while (child = tag()) {
|
||||
node.children.push(child)
|
||||
}
|
||||
|
||||
// closing
|
||||
match(/^<\/[\w-:.]+>\s*/)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Text content.
|
||||
*/
|
||||
|
||||
function content() {
|
||||
const m = match(/^([^<]*)/)
|
||||
if (m) return m[1]
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute.
|
||||
*/
|
||||
|
||||
function attribute() {
|
||||
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
||||
if (!m) return
|
||||
return {name: m[1], value: strip(m[2])}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip quotes from `val`.
|
||||
*/
|
||||
|
||||
function strip(val) {
|
||||
return val.replace(/^['"]|['"]$/g, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Match `re` and advance the string.
|
||||
*/
|
||||
|
||||
function match(re) {
|
||||
const m = xml.match(re)
|
||||
if (!m) return
|
||||
xml = xml.slice(m[0].length)
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* End-of-source.
|
||||
*/
|
||||
|
||||
function eos() {
|
||||
return xml.length == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for `prefix`.
|
||||
*/
|
||||
|
||||
function is(prefix) {
|
||||
return xml.indexOf(prefix) == 0
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parse
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 3 */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
const Block = __webpack_require__(4)
|
||||
const {splitLineToCamelCase} = __webpack_require__(0)
|
||||
|
||||
class Element extends Block {
|
||||
constructor(prop) {
|
||||
super(prop.style)
|
||||
this.name = prop.name
|
||||
this.attributes = prop.attributes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Widget {
|
||||
constructor(xom, style) {
|
||||
this.xom = xom
|
||||
this.style = style
|
||||
|
||||
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color']
|
||||
}
|
||||
|
||||
init() {
|
||||
this.container = this.create(this.xom)
|
||||
this.container.layout()
|
||||
|
||||
this.inheritStyle(this.container)
|
||||
return this.container
|
||||
}
|
||||
|
||||
// 继承父节点的样式
|
||||
inheritStyle(node) {
|
||||
const parent = node.parent || null
|
||||
const children = node.children || {}
|
||||
const computedStyle = node.computedStyle
|
||||
|
||||
if (parent) {
|
||||
this.inheritProps.forEach(prop => {
|
||||
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop]
|
||||
})
|
||||
}
|
||||
|
||||
Object.values(children).forEach(child => {
|
||||
this.inheritStyle(child)
|
||||
})
|
||||
}
|
||||
|
||||
create(node) {
|
||||
let classNames = (node.attributes.class || '').split(' ')
|
||||
classNames = classNames.map(item => splitLineToCamelCase(item.trim()))
|
||||
const style = {}
|
||||
classNames.forEach(item => {
|
||||
Object.assign(style, this.style[item] || {})
|
||||
})
|
||||
|
||||
const args = {name: node.name, style}
|
||||
|
||||
const attrs = Object.keys(node.attributes)
|
||||
const attributes = {}
|
||||
for (const attr of attrs) {
|
||||
const value = node.attributes[attr]
|
||||
const CamelAttr = splitLineToCamelCase(attr)
|
||||
|
||||
if (value === '' || value === 'true') {
|
||||
attributes[CamelAttr] = true
|
||||
} else if (value === 'false') {
|
||||
attributes[CamelAttr] = false
|
||||
} else {
|
||||
attributes[CamelAttr] = value
|
||||
}
|
||||
}
|
||||
attributes.text = node.content
|
||||
args.attributes = attributes
|
||||
const element = new Element(args)
|
||||
node.children.forEach(childNode => {
|
||||
const childElement = this.create(childNode)
|
||||
element.add(childElement)
|
||||
})
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Widget}
|
||||
|
||||
|
||||
/***/ }),
|
||||
/* 4 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
module.exports = require("widget-ui");
|
||||
|
||||
/***/ }),
|
||||
/* 5 */
|
||||
/***/ (function(module, exports) {
|
||||
|
||||
class Draw {
|
||||
constructor(context, canvas, use2dCanvas = false) {
|
||||
this.ctx = context
|
||||
this.canvas = canvas || null
|
||||
this.use2dCanvas = use2dCanvas
|
||||
}
|
||||
|
||||
roundRect(x, y, w, h, r, fill = true, stroke = false) {
|
||||
if (r < 0) return
|
||||
const ctx = this.ctx
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
|
||||
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
|
||||
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
|
||||
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
|
||||
ctx.lineTo(x, y + r)
|
||||
if (stroke) ctx.stroke()
|
||||
if (fill) ctx.fill()
|
||||
}
|
||||
|
||||
drawView(box, style) {
|
||||
const ctx = this.ctx
|
||||
const {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
const {
|
||||
borderRadius = 0,
|
||||
borderWidth = 0,
|
||||
borderColor,
|
||||
color = '#000',
|
||||
backgroundColor = 'transparent',
|
||||
} = style
|
||||
ctx.save()
|
||||
// 外环
|
||||
if (borderWidth > 0) {
|
||||
ctx.fillStyle = borderColor || color
|
||||
this.roundRect(x, y, w, h, borderRadius)
|
||||
}
|
||||
|
||||
// 内环
|
||||
ctx.fillStyle = backgroundColor
|
||||
const innerWidth = w - 2 * borderWidth
|
||||
const innerHeight = h - 2 * borderWidth
|
||||
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
|
||||
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
async drawImage(img, box, style) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const ctx = this.ctx
|
||||
const canvas = this.canvas
|
||||
|
||||
const {
|
||||
borderRadius = 0
|
||||
} = style
|
||||
const {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
ctx.save()
|
||||
this.roundRect(x, y, w, h, borderRadius, false, false)
|
||||
ctx.clip()
|
||||
|
||||
const _drawImage = (img) => {
|
||||
if (this.use2dCanvas) {
|
||||
const Image = canvas.createImage()
|
||||
Image.onload = () => {
|
||||
ctx.drawImage(Image, x, y, w, h)
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
Image.onerror = () => { reject(new Error(`createImage fail: ${img}`)) }
|
||||
Image.src = img
|
||||
} else {
|
||||
ctx.drawImage(img, x, y, w, h)
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
const isTempFile = /^wxfile:\/\//.test(img)
|
||||
const isNetworkFile = /^https?:\/\//.test(img)
|
||||
|
||||
if (isTempFile) {
|
||||
_drawImage(img)
|
||||
} else if (isNetworkFile) {
|
||||
wx.downloadFile({
|
||||
url: img,
|
||||
success(res) {
|
||||
if (res.statusCode === 200) {
|
||||
_drawImage(res.tempFilePath)
|
||||
} else {
|
||||
reject(new Error(`downloadFile:fail ${img}`))
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
reject(new Error(`downloadFile:fail ${img}`))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject(new Error(`image format error: ${img}`))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
drawText(text, box, style) {
|
||||
const ctx = this.ctx
|
||||
let {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
let {
|
||||
color = '#000',
|
||||
lineHeight = '1.4em',
|
||||
fontSize = 14,
|
||||
textAlign = 'left',
|
||||
verticalAlign = 'top',
|
||||
backgroundColor = 'transparent'
|
||||
} = style
|
||||
|
||||
if (typeof lineHeight === 'string') { // 2em
|
||||
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize)
|
||||
}
|
||||
if (!text || (lineHeight > h)) return
|
||||
|
||||
ctx.save()
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.font = `${fontSize}px sans-serif`
|
||||
ctx.textAlign = textAlign
|
||||
|
||||
// 背景色
|
||||
ctx.fillStyle = backgroundColor
|
||||
this.roundRect(x, y, w, h, 0)
|
||||
|
||||
// 文字颜色
|
||||
ctx.fillStyle = color
|
||||
|
||||
// 水平布局
|
||||
switch (textAlign) {
|
||||
case 'left':
|
||||
break
|
||||
case 'center':
|
||||
x += 0.5 * w
|
||||
break
|
||||
case 'right':
|
||||
x += w
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
const textWidth = ctx.measureText(text).width
|
||||
const actualHeight = Math.ceil(textWidth / w) * lineHeight
|
||||
let paddingTop = Math.ceil((h - actualHeight) / 2)
|
||||
if (paddingTop < 0) paddingTop = 0
|
||||
|
||||
// 垂直布局
|
||||
switch (verticalAlign) {
|
||||
case 'top':
|
||||
break
|
||||
case 'middle':
|
||||
y += paddingTop
|
||||
break
|
||||
case 'bottom':
|
||||
y += 2 * paddingTop
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
|
||||
|
||||
// 不超过一行
|
||||
if (textWidth <= w) {
|
||||
ctx.fillText(text, x, y + inlinePaddingTop)
|
||||
return
|
||||
}
|
||||
|
||||
// 多行文本
|
||||
const chars = text.split('')
|
||||
const _y = y
|
||||
|
||||
// 逐行绘制
|
||||
let line = ''
|
||||
for (const ch of chars) {
|
||||
const testLine = line + ch
|
||||
const testWidth = ctx.measureText(testLine).width
|
||||
|
||||
if (testWidth > w) {
|
||||
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||
y += lineHeight
|
||||
line = ch
|
||||
if ((y + lineHeight) > (_y + h)) break
|
||||
} else {
|
||||
line = testLine
|
||||
}
|
||||
}
|
||||
|
||||
// 避免溢出
|
||||
if ((y + lineHeight) <= (_y + h)) {
|
||||
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||
}
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
async drawNode(element) {
|
||||
const {layoutBox, computedStyle, name} = element
|
||||
const {src, text} = element.attributes
|
||||
if (name === 'view') {
|
||||
this.drawView(layoutBox, computedStyle)
|
||||
} else if (name === 'image') {
|
||||
await this.drawImage(src, layoutBox, computedStyle)
|
||||
} else if (name === 'text') {
|
||||
this.drawText(text, layoutBox, computedStyle)
|
||||
}
|
||||
const childs = Object.values(element.children)
|
||||
for (const child of childs) {
|
||||
await this.drawNode(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
Draw
|
||||
}
|
||||
|
||||
|
||||
/***/ })
|
||||
/******/ ]);
|
||||
});
|
4
node_modules/wxml-to-canvas/miniprogram_dist/index.json
generated
vendored
4
node_modules/wxml-to-canvas/miniprogram_dist/index.json
generated
vendored
@ -1,4 +0,0 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
2
node_modules/wxml-to-canvas/miniprogram_dist/index.wxml
generated
vendored
2
node_modules/wxml-to-canvas/miniprogram_dist/index.wxml
generated
vendored
@ -1,2 +0,0 @@
|
||||
<canvas wx:if="{{use2dCanvas}}" id="weui-canvas" type="2d" style="width: {{width}}px; height: {{height}}px;"></canvas>
|
||||
<canvas wx:else canvas-id="weui-canvas" style="width: {{width}}px; height: {{height}}px;"></canvas>
|
0
node_modules/wxml-to-canvas/miniprogram_dist/index.wxss
generated
vendored
0
node_modules/wxml-to-canvas/miniprogram_dist/index.wxss
generated
vendored
57
node_modules/wxml-to-canvas/miniprogram_dist/utils.js
generated
vendored
57
node_modules/wxml-to-canvas/miniprogram_dist/utils.js
generated
vendored
@ -1,57 +0,0 @@
|
||||
const hex = (color) => {
|
||||
let result = null
|
||||
|
||||
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||
return color
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||
return '#' + result[2].split(',').map((part, index) => {
|
||||
part = part.trim()
|
||||
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||
part = part.toString(16)
|
||||
if (part.length === 1) {
|
||||
part = '0' + part
|
||||
}
|
||||
return part
|
||||
}).join('')
|
||||
} else {
|
||||
return '#00000000'
|
||||
}
|
||||
}
|
||||
|
||||
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||
if (index === 0) {
|
||||
return part
|
||||
}
|
||||
return part[0].toUpperCase() + part.slice(1)
|
||||
}).join('')
|
||||
|
||||
const compareVersion = (v1, v2) => {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hex,
|
||||
splitLineToCamelCase,
|
||||
compareVersion
|
||||
}
|
66
node_modules/wxml-to-canvas/package.json
generated
vendored
66
node_modules/wxml-to-canvas/package.json
generated
vendored
@ -1,66 +0,0 @@
|
||||
{
|
||||
"name": "wxml-to-canvas",
|
||||
"version": "1.1.1",
|
||||
"description": "",
|
||||
"main": "miniprogram_dist/index.js",
|
||||
"scripts": {
|
||||
"dev": "gulp dev --develop",
|
||||
"watch": "gulp watch --develop --watch",
|
||||
"build": "gulp",
|
||||
"dist": "npm run build",
|
||||
"clean-dev": "gulp clean --develop",
|
||||
"clean": "gulp clean",
|
||||
"test": "jest --bail",
|
||||
"test-debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand --bail",
|
||||
"coverage": "jest ./test/* --coverage --bail",
|
||||
"lint": "eslint \"src/**/*.js\" --fix",
|
||||
"lint-tools": "eslint \"tools/**/*.js\" --rule \"import/no-extraneous-dependencies: false\" --fix"
|
||||
},
|
||||
"miniprogram": "miniprogram_dist",
|
||||
"jest": {
|
||||
"testEnvironment": "jsdom",
|
||||
"testURL": "https://jest.test",
|
||||
"collectCoverageFrom": [
|
||||
"src/**/*.js"
|
||||
],
|
||||
"moduleDirectories": [
|
||||
"node_modules",
|
||||
"src"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": ""
|
||||
},
|
||||
"author": "sanfordsun",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"colors": "^1.3.1",
|
||||
"eslint": "^5.14.1",
|
||||
"eslint-config-airbnb-base": "13.1.0",
|
||||
"eslint-loader": "^2.1.2",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
"eslint-plugin-node": "^7.0.1",
|
||||
"eslint-plugin-promise": "^3.8.0",
|
||||
"gulp": "^4.0.0",
|
||||
"gulp-clean": "^0.4.0",
|
||||
"gulp-if": "^2.0.2",
|
||||
"gulp-install": "^1.1.0",
|
||||
"gulp-less": "^4.0.1",
|
||||
"gulp-rename": "^1.4.0",
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"jest": "^23.5.0",
|
||||
"miniprogram-simulate": "^1.0.0",
|
||||
"through2": "^2.0.3",
|
||||
"vinyl": "^2.2.0",
|
||||
"webpack": "^4.29.5",
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-node-externals": "^1.7.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"widget-ui": "^1.0.2"
|
||||
},
|
||||
"__npminstall_done": true,
|
||||
"_from": "wxml-to-canvas@1.1.1",
|
||||
"_resolved": "https://registry.npmmirror.com/wxml-to-canvas/-/wxml-to-canvas-1.1.1.tgz"
|
||||
}
|
225
node_modules/wxml-to-canvas/src/draw.js
generated
vendored
225
node_modules/wxml-to-canvas/src/draw.js
generated
vendored
@ -1,225 +0,0 @@
|
||||
class Draw {
|
||||
constructor(context, canvas, use2dCanvas = false) {
|
||||
this.ctx = context
|
||||
this.canvas = canvas || null
|
||||
this.use2dCanvas = use2dCanvas
|
||||
}
|
||||
|
||||
roundRect(x, y, w, h, r, fill = true, stroke = false) {
|
||||
if (r < 0) return
|
||||
const ctx = this.ctx
|
||||
|
||||
ctx.beginPath()
|
||||
ctx.arc(x + r, y + r, r, Math.PI, Math.PI * 3 / 2)
|
||||
ctx.arc(x + w - r, y + r, r, Math.PI * 3 / 2, 0)
|
||||
ctx.arc(x + w - r, y + h - r, r, 0, Math.PI / 2)
|
||||
ctx.arc(x + r, y + h - r, r, Math.PI / 2, Math.PI)
|
||||
ctx.lineTo(x, y + r)
|
||||
if (stroke) ctx.stroke()
|
||||
if (fill) ctx.fill()
|
||||
}
|
||||
|
||||
drawView(box, style) {
|
||||
const ctx = this.ctx
|
||||
const {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
const {
|
||||
borderRadius = 0,
|
||||
borderWidth = 0,
|
||||
borderColor,
|
||||
color = '#000',
|
||||
backgroundColor = 'transparent',
|
||||
} = style
|
||||
ctx.save()
|
||||
// 外环
|
||||
if (borderWidth > 0) {
|
||||
ctx.fillStyle = borderColor || color
|
||||
this.roundRect(x, y, w, h, borderRadius)
|
||||
}
|
||||
|
||||
// 内环
|
||||
ctx.fillStyle = backgroundColor
|
||||
const innerWidth = w - 2 * borderWidth
|
||||
const innerHeight = h - 2 * borderWidth
|
||||
const innerRadius = borderRadius - borderWidth >= 0 ? borderRadius - borderWidth : 0
|
||||
this.roundRect(x + borderWidth, y + borderWidth, innerWidth, innerHeight, innerRadius)
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
async drawImage(img, box, style) {
|
||||
await new Promise((resolve, reject) => {
|
||||
const ctx = this.ctx
|
||||
const canvas = this.canvas
|
||||
|
||||
const {
|
||||
borderRadius = 0
|
||||
} = style
|
||||
const {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
ctx.save()
|
||||
this.roundRect(x, y, w, h, borderRadius, false, false)
|
||||
ctx.clip()
|
||||
|
||||
const _drawImage = (img) => {
|
||||
if (this.use2dCanvas) {
|
||||
const Image = canvas.createImage()
|
||||
Image.onload = () => {
|
||||
ctx.drawImage(Image, x, y, w, h)
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
Image.onerror = () => { reject(new Error(`createImage fail: ${img}`)) }
|
||||
Image.src = img
|
||||
} else {
|
||||
ctx.drawImage(img, x, y, w, h)
|
||||
ctx.restore()
|
||||
resolve()
|
||||
}
|
||||
}
|
||||
|
||||
const isTempFile = /^wxfile:\/\//.test(img)
|
||||
const isNetworkFile = /^https?:\/\//.test(img)
|
||||
|
||||
if (isTempFile) {
|
||||
_drawImage(img)
|
||||
} else if (isNetworkFile) {
|
||||
wx.downloadFile({
|
||||
url: img,
|
||||
success(res) {
|
||||
if (res.statusCode === 200) {
|
||||
_drawImage(res.tempFilePath)
|
||||
} else {
|
||||
reject(new Error(`downloadFile:fail ${img}`))
|
||||
}
|
||||
},
|
||||
fail() {
|
||||
reject(new Error(`downloadFile:fail ${img}`))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject(new Error(`image format error: ${img}`))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
drawText(text, box, style) {
|
||||
const ctx = this.ctx
|
||||
let {
|
||||
left: x, top: y, width: w, height: h
|
||||
} = box
|
||||
let {
|
||||
color = '#000',
|
||||
lineHeight = '1.4em',
|
||||
fontSize = 14,
|
||||
textAlign = 'left',
|
||||
verticalAlign = 'top',
|
||||
backgroundColor = 'transparent'
|
||||
} = style
|
||||
|
||||
if (typeof lineHeight === 'string') { // 2em
|
||||
lineHeight = Math.ceil(parseFloat(lineHeight.replace('em')) * fontSize)
|
||||
}
|
||||
if (!text || (lineHeight > h)) return
|
||||
|
||||
ctx.save()
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.font = `${fontSize}px sans-serif`
|
||||
ctx.textAlign = textAlign
|
||||
|
||||
// 背景色
|
||||
ctx.fillStyle = backgroundColor
|
||||
this.roundRect(x, y, w, h, 0)
|
||||
|
||||
// 文字颜色
|
||||
ctx.fillStyle = color
|
||||
|
||||
// 水平布局
|
||||
switch (textAlign) {
|
||||
case 'left':
|
||||
break
|
||||
case 'center':
|
||||
x += 0.5 * w
|
||||
break
|
||||
case 'right':
|
||||
x += w
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
const textWidth = ctx.measureText(text).width
|
||||
const actualHeight = Math.ceil(textWidth / w) * lineHeight
|
||||
let paddingTop = Math.ceil((h - actualHeight) / 2)
|
||||
if (paddingTop < 0) paddingTop = 0
|
||||
|
||||
// 垂直布局
|
||||
switch (verticalAlign) {
|
||||
case 'top':
|
||||
break
|
||||
case 'middle':
|
||||
y += paddingTop
|
||||
break
|
||||
case 'bottom':
|
||||
y += 2 * paddingTop
|
||||
break
|
||||
default: break
|
||||
}
|
||||
|
||||
const inlinePaddingTop = Math.ceil((lineHeight - fontSize) / 2)
|
||||
|
||||
// 不超过一行
|
||||
if (textWidth <= w) {
|
||||
ctx.fillText(text, x, y + inlinePaddingTop)
|
||||
return
|
||||
}
|
||||
|
||||
// 多行文本
|
||||
const chars = text.split('')
|
||||
const _y = y
|
||||
|
||||
// 逐行绘制
|
||||
let line = ''
|
||||
for (const ch of chars) {
|
||||
const testLine = line + ch
|
||||
const testWidth = ctx.measureText(testLine).width
|
||||
|
||||
if (testWidth > w) {
|
||||
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||
y += lineHeight
|
||||
line = ch
|
||||
if ((y + lineHeight) > (_y + h)) break
|
||||
} else {
|
||||
line = testLine
|
||||
}
|
||||
}
|
||||
|
||||
// 避免溢出
|
||||
if ((y + lineHeight) <= (_y + h)) {
|
||||
ctx.fillText(line, x, y + inlinePaddingTop)
|
||||
}
|
||||
ctx.restore()
|
||||
}
|
||||
|
||||
async drawNode(element) {
|
||||
const {layoutBox, computedStyle, name} = element
|
||||
const {src, text} = element.attributes
|
||||
if (name === 'view') {
|
||||
this.drawView(layoutBox, computedStyle)
|
||||
} else if (name === 'image') {
|
||||
await this.drawImage(src, layoutBox, computedStyle)
|
||||
} else if (name === 'text') {
|
||||
this.drawText(text, layoutBox, computedStyle)
|
||||
}
|
||||
const childs = Object.values(element.children)
|
||||
for (const child of childs) {
|
||||
await this.drawNode(child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
Draw
|
||||
}
|
117
node_modules/wxml-to-canvas/src/index.js
generated
vendored
117
node_modules/wxml-to-canvas/src/index.js
generated
vendored
@ -1,117 +0,0 @@
|
||||
|
||||
const xmlParse = require('./xml-parser')
|
||||
const {Widget} = require('./widget')
|
||||
const {Draw} = require('./draw')
|
||||
const {compareVersion} = require('./utils')
|
||||
|
||||
const canvasId = 'weui-canvas'
|
||||
|
||||
Component({
|
||||
properties: {
|
||||
width: {
|
||||
type: Number,
|
||||
value: 400
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
value: 300
|
||||
}
|
||||
},
|
||||
data: {
|
||||
use2dCanvas: false, // 2.9.2 后可用canvas 2d 接口
|
||||
},
|
||||
lifetimes: {
|
||||
attached() {
|
||||
const {SDKVersion, pixelRatio: dpr} = wx.getSystemInfoSync()
|
||||
const use2dCanvas = compareVersion(SDKVersion, '2.9.2') >= 0
|
||||
this.dpr = dpr
|
||||
this.setData({use2dCanvas}, () => {
|
||||
if (use2dCanvas) {
|
||||
const query = this.createSelectorQuery()
|
||||
query.select(`#${canvasId}`)
|
||||
.fields({node: true, size: true})
|
||||
.exec(res => {
|
||||
const canvas = res[0].node
|
||||
const ctx = canvas.getContext('2d')
|
||||
canvas.width = res[0].width * dpr
|
||||
canvas.height = res[0].height * dpr
|
||||
ctx.scale(dpr, dpr)
|
||||
this.ctx = ctx
|
||||
this.canvas = canvas
|
||||
})
|
||||
} else {
|
||||
this.ctx = wx.createCanvasContext(canvasId, this)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async renderToCanvas(args) {
|
||||
const {wxml, style} = args
|
||||
const ctx = this.ctx
|
||||
const canvas = this.canvas
|
||||
const use2dCanvas = this.data.use2dCanvas
|
||||
|
||||
if (use2dCanvas && !canvas) {
|
||||
return Promise.reject(new Error('renderToCanvas: fail canvas has not been created'))
|
||||
}
|
||||
|
||||
ctx.clearRect(0, 0, this.data.width, this.data.height)
|
||||
const {root: xom} = xmlParse(wxml)
|
||||
|
||||
const widget = new Widget(xom, style)
|
||||
const container = widget.init()
|
||||
this.boundary = {
|
||||
top: container.layoutBox.top,
|
||||
left: container.layoutBox.left,
|
||||
width: container.computedStyle.width,
|
||||
height: container.computedStyle.height,
|
||||
}
|
||||
const draw = new Draw(ctx, canvas, use2dCanvas)
|
||||
await draw.drawNode(container)
|
||||
|
||||
if (!use2dCanvas) {
|
||||
await this.canvasDraw(ctx)
|
||||
}
|
||||
return Promise.resolve(container)
|
||||
},
|
||||
|
||||
canvasDraw(ctx, reserve) {
|
||||
return new Promise(resolve => {
|
||||
ctx.draw(reserve, () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
canvasToTempFilePath(args = {}) {
|
||||
const use2dCanvas = this.data.use2dCanvas
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const {
|
||||
top, left, width, height
|
||||
} = this.boundary
|
||||
|
||||
const copyArgs = {
|
||||
x: left,
|
||||
y: top,
|
||||
width,
|
||||
height,
|
||||
destWidth: width * this.dpr,
|
||||
destHeight: height * this.dpr,
|
||||
canvasId,
|
||||
fileType: args.fileType || 'png',
|
||||
quality: args.quality || 1,
|
||||
success: resolve,
|
||||
fail: reject
|
||||
}
|
||||
|
||||
if (use2dCanvas) {
|
||||
delete copyArgs.canvasId
|
||||
copyArgs.canvas = this.canvas
|
||||
}
|
||||
wx.canvasToTempFilePath(copyArgs, this)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
4
node_modules/wxml-to-canvas/src/index.json
generated
vendored
4
node_modules/wxml-to-canvas/src/index.json
generated
vendored
@ -1,4 +0,0 @@
|
||||
{
|
||||
"component": true,
|
||||
"usingComponents": {}
|
||||
}
|
2
node_modules/wxml-to-canvas/src/index.wxml
generated
vendored
2
node_modules/wxml-to-canvas/src/index.wxml
generated
vendored
@ -1,2 +0,0 @@
|
||||
<canvas wx:if="{{use2dCanvas}}" id="weui-canvas" type="2d" style="width: {{width}}px; height: {{height}}px;"></canvas>
|
||||
<canvas wx:else canvas-id="weui-canvas" style="width: {{width}}px; height: {{height}}px;"></canvas>
|
0
node_modules/wxml-to-canvas/src/index.wxss
generated
vendored
0
node_modules/wxml-to-canvas/src/index.wxss
generated
vendored
57
node_modules/wxml-to-canvas/src/utils.js
generated
vendored
57
node_modules/wxml-to-canvas/src/utils.js
generated
vendored
@ -1,57 +0,0 @@
|
||||
const hex = (color) => {
|
||||
let result = null
|
||||
|
||||
if (/^#/.test(color) && (color.length === 7 || color.length === 9)) {
|
||||
return color
|
||||
// eslint-disable-next-line no-cond-assign
|
||||
} else if ((result = /^(rgb|rgba)\((.+)\)/.exec(color)) !== null) {
|
||||
return '#' + result[2].split(',').map((part, index) => {
|
||||
part = part.trim()
|
||||
part = index === 3 ? Math.floor(parseFloat(part) * 255) : parseInt(part, 10)
|
||||
part = part.toString(16)
|
||||
if (part.length === 1) {
|
||||
part = '0' + part
|
||||
}
|
||||
return part
|
||||
}).join('')
|
||||
} else {
|
||||
return '#00000000'
|
||||
}
|
||||
}
|
||||
|
||||
const splitLineToCamelCase = (str) => str.split('-').map((part, index) => {
|
||||
if (index === 0) {
|
||||
return part
|
||||
}
|
||||
return part[0].toUpperCase() + part.slice(1)
|
||||
}).join('')
|
||||
|
||||
const compareVersion = (v1, v2) => {
|
||||
v1 = v1.split('.')
|
||||
v2 = v2.split('.')
|
||||
const len = Math.max(v1.length, v2.length)
|
||||
while (v1.length < len) {
|
||||
v1.push('0')
|
||||
}
|
||||
while (v2.length < len) {
|
||||
v2.push('0')
|
||||
}
|
||||
for (let i = 0; i < len; i++) {
|
||||
const num1 = parseInt(v1[i], 10)
|
||||
const num2 = parseInt(v2[i], 10)
|
||||
|
||||
if (num1 > num2) {
|
||||
return 1
|
||||
} else if (num1 < num2) {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hex,
|
||||
splitLineToCamelCase,
|
||||
compareVersion
|
||||
}
|
81
node_modules/wxml-to-canvas/src/widget.js
generated
vendored
81
node_modules/wxml-to-canvas/src/widget.js
generated
vendored
@ -1,81 +0,0 @@
|
||||
const Block = require('widget-ui')
|
||||
const {splitLineToCamelCase} = require('./utils')
|
||||
|
||||
class Element extends Block {
|
||||
constructor(prop) {
|
||||
super(prop.style)
|
||||
this.name = prop.name
|
||||
this.attributes = prop.attributes
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Widget {
|
||||
constructor(xom, style) {
|
||||
this.xom = xom
|
||||
this.style = style
|
||||
|
||||
this.inheritProps = ['fontSize', 'lineHeight', 'textAlign', 'verticalAlign', 'color']
|
||||
}
|
||||
|
||||
init() {
|
||||
this.container = this.create(this.xom)
|
||||
this.container.layout()
|
||||
|
||||
this.inheritStyle(this.container)
|
||||
return this.container
|
||||
}
|
||||
|
||||
// 继承父节点的样式
|
||||
inheritStyle(node) {
|
||||
const parent = node.parent || null
|
||||
const children = node.children || {}
|
||||
const computedStyle = node.computedStyle
|
||||
|
||||
if (parent) {
|
||||
this.inheritProps.forEach(prop => {
|
||||
computedStyle[prop] = computedStyle[prop] || parent.computedStyle[prop]
|
||||
})
|
||||
}
|
||||
|
||||
Object.values(children).forEach(child => {
|
||||
this.inheritStyle(child)
|
||||
})
|
||||
}
|
||||
|
||||
create(node) {
|
||||
let classNames = (node.attributes.class || '').split(' ')
|
||||
classNames = classNames.map(item => splitLineToCamelCase(item.trim()))
|
||||
const style = {}
|
||||
classNames.forEach(item => {
|
||||
Object.assign(style, this.style[item] || {})
|
||||
})
|
||||
|
||||
const args = {name: node.name, style}
|
||||
|
||||
const attrs = Object.keys(node.attributes)
|
||||
const attributes = {}
|
||||
for (const attr of attrs) {
|
||||
const value = node.attributes[attr]
|
||||
const CamelAttr = splitLineToCamelCase(attr)
|
||||
|
||||
if (value === '' || value === 'true') {
|
||||
attributes[CamelAttr] = true
|
||||
} else if (value === 'false') {
|
||||
attributes[CamelAttr] = false
|
||||
} else {
|
||||
attributes[CamelAttr] = value
|
||||
}
|
||||
}
|
||||
attributes.text = node.content
|
||||
args.attributes = attributes
|
||||
const element = new Element(args)
|
||||
node.children.forEach(childNode => {
|
||||
const childElement = this.create(childNode)
|
||||
element.add(childElement)
|
||||
})
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {Widget}
|
164
node_modules/wxml-to-canvas/src/xml-parser.js
generated
vendored
164
node_modules/wxml-to-canvas/src/xml-parser.js
generated
vendored
@ -1,164 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Expose `parse`.
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Parse the given string of `xml`.
|
||||
*
|
||||
* @param {String} xml
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function parse(xml) {
|
||||
xml = xml.trim()
|
||||
|
||||
// strip comments
|
||||
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
||||
|
||||
return document()
|
||||
|
||||
/**
|
||||
* XML document.
|
||||
*/
|
||||
|
||||
function document() {
|
||||
return {
|
||||
declaration: declaration(),
|
||||
root: tag()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Declaration.
|
||||
*/
|
||||
|
||||
function declaration() {
|
||||
const m = match(/^<\?xml\s*/)
|
||||
if (!m) return
|
||||
|
||||
// tag
|
||||
const node = {
|
||||
attributes: {}
|
||||
}
|
||||
|
||||
// attributes
|
||||
while (!(eos() || is('?>'))) {
|
||||
const attr = attribute()
|
||||
if (!attr) return node
|
||||
node.attributes[attr.name] = attr.value
|
||||
}
|
||||
|
||||
match(/\?>\s*/)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Tag.
|
||||
*/
|
||||
|
||||
function tag() {
|
||||
const m = match(/^<([\w-:.]+)\s*/)
|
||||
if (!m) return
|
||||
|
||||
// name
|
||||
const node = {
|
||||
name: m[1],
|
||||
attributes: {},
|
||||
children: []
|
||||
}
|
||||
|
||||
// attributes
|
||||
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
||||
const attr = attribute()
|
||||
if (!attr) return node
|
||||
node.attributes[attr.name] = attr.value
|
||||
}
|
||||
|
||||
// self closing tag
|
||||
if (match(/^\s*\/>\s*/)) {
|
||||
return node
|
||||
}
|
||||
|
||||
match(/\??>\s*/)
|
||||
|
||||
// content
|
||||
node.content = content()
|
||||
|
||||
// children
|
||||
let child
|
||||
while (child = tag()) {
|
||||
node.children.push(child)
|
||||
}
|
||||
|
||||
// closing
|
||||
match(/^<\/[\w-:.]+>\s*/)
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
/**
|
||||
* Text content.
|
||||
*/
|
||||
|
||||
function content() {
|
||||
const m = match(/^([^<]*)/)
|
||||
if (m) return m[1]
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute.
|
||||
*/
|
||||
|
||||
function attribute() {
|
||||
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
||||
if (!m) return
|
||||
return {name: m[1], value: strip(m[2])}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip quotes from `val`.
|
||||
*/
|
||||
|
||||
function strip(val) {
|
||||
return val.replace(/^['"]|['"]$/g, '')
|
||||
}
|
||||
|
||||
/**
|
||||
* Match `re` and advance the string.
|
||||
*/
|
||||
|
||||
function match(re) {
|
||||
const m = xml.match(re)
|
||||
if (!m) return
|
||||
xml = xml.slice(m[0].length)
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* End-of-source.
|
||||
*/
|
||||
|
||||
function eos() {
|
||||
return xml.length == 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for `prefix`.
|
||||
*/
|
||||
|
||||
function is(prefix) {
|
||||
return xml.indexOf(prefix) == 0
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = parse
|
22
package-lock.json
generated
Normal file
22
package-lock.json
generated
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "小程序 - 租房",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/.store/eventemitter3@4.0.7/node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"extraneous": true,
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"assume": "^2.2.0",
|
||||
"browserify": "^16.5.0",
|
||||
"mocha": "^8.0.1",
|
||||
"nyc": "^15.1.0",
|
||||
"pre-commit": "^1.2.0",
|
||||
"sauce-browsers": "^2.0.0",
|
||||
"sauce-test": "^1.3.3",
|
||||
"uglify-js": "^3.9.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"widget-ui": "^1.0.2",
|
||||
"wxml-to-canvas": "^1.1.1"
|
||||
}
|
||||
}
|
||||
{}
|
||||
|
@ -66,6 +66,8 @@ Page({
|
||||
|
||||
isShowVideo: true, // 是否显示 视频
|
||||
|
||||
shareImage: "", // 分享图片链接
|
||||
|
||||
},
|
||||
timer: null,
|
||||
headHeight: 0,
|
||||
@ -174,13 +176,20 @@ Page({
|
||||
// 来自页面内转发按钮
|
||||
if (res.from === 'button') var types = res.from === 'button' ? 'share_btn' : 'show';
|
||||
|
||||
let that = this;
|
||||
var title = that.data.data.title;
|
||||
if (that.data.data.isquarantine) title = this.data.listTab.quarantineLists + '-' + that.data.data.title
|
||||
// let that = this;
|
||||
// var title = that.data.data.title;
|
||||
// if (that.data.data.isquarantine) title = this.data.listTab.quarantineLists + '-' + that.data.data.title
|
||||
|
||||
const data = this.data.data
|
||||
|
||||
let title = "";
|
||||
if (data.sharetitle) title = '香港租房 | ' + data.sharetitle
|
||||
else title = '香港租房 | ' + data.title
|
||||
|
||||
return {
|
||||
title,
|
||||
imageUrl: that.data.data.thumbnail,
|
||||
// imageUrl: that.data.data.thumbnail,
|
||||
imageUrl: this.data.shareImage || '',
|
||||
success: function (res) {
|
||||
miucms.share(app, types)
|
||||
},
|
||||
@ -188,13 +197,16 @@ Page({
|
||||
|
||||
},
|
||||
onShareTimeline() {
|
||||
let that = this;
|
||||
var title = that.data.data.title;
|
||||
if (that.data.data.isquarantine) title = this.data.listTab.quarantineLists + '-' + that.data.data.title
|
||||
const data = this.data.data
|
||||
let title = "";
|
||||
if (data.sharetitle) title = '香港租房 | ' + data.sharetitle
|
||||
else title = '香港租房 | ' + data.title
|
||||
|
||||
// if (that.data.data.isquarantine) title = this.data.listTab.quarantineLists + '-' + that.data.data.title
|
||||
|
||||
return {
|
||||
title,
|
||||
imageUrl: that.data.data.thumbnail,
|
||||
imageUrl: this.data.shareImage || '',
|
||||
}
|
||||
},
|
||||
|
||||
@ -294,16 +306,8 @@ Page({
|
||||
isloding: false
|
||||
})
|
||||
|
||||
this.widget = this.selectComponent('.widget')
|
||||
console.log(this.widget);
|
||||
setTimeout(() => {
|
||||
this.setwidget()
|
||||
}, 800)
|
||||
|
||||
|
||||
if (data.withsameapartments > 0) this.getList()
|
||||
// this.handleSwiperInit()
|
||||
|
||||
this.drawPoster()
|
||||
}
|
||||
}).catch(res => {
|
||||
@ -321,302 +325,27 @@ Page({
|
||||
})
|
||||
},
|
||||
|
||||
setwidget() {
|
||||
wx.getImageInfo({
|
||||
src: "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5",
|
||||
success: res => {
|
||||
console.log("res", res.path);
|
||||
drawPoster() {
|
||||
const user = app.globalData.user
|
||||
const data = this.data.data
|
||||
const roomList = this.data.roomList || []
|
||||
let title = `${ roomList.length > 1 ? '多房型' : '' }`
|
||||
|
||||
let wxml = `<view class="placard">
|
||||
<image class="placard-tag" src="https://app.gter.net/image/miniApp/HKRenting/high-quality-tag.png" mode="widthFix"></image>
|
||||
<view class="placard-info">
|
||||
<text class="text">肖荣豪 为你推荐</text>
|
||||
</view>
|
||||
const price = Math.min(...roomList.map(item => item.price));
|
||||
title += ` HK$${ price }起`
|
||||
|
||||
<view class="position">
|
||||
<image class="position-icon" src="https://app.gter.net/image/miniApp/HKRenting/position-icon.svg" mode="widthFix"></image>
|
||||
<view class="position-text">
|
||||
香港 | 多房型 HK$5600起
|
||||
</view>
|
||||
<image class="position-arrow" src="https://app.gter.net/image/miniApp/HKRenting/arrow-round-yellow.svg"></image>
|
||||
</view>
|
||||
</view>`
|
||||
|
||||
|
||||
let style = {
|
||||
placard: {
|
||||
width: "210",
|
||||
height: "158",
|
||||
position: "relative",
|
||||
// paddingTop: "3",
|
||||
backgroundColor: "azure"
|
||||
},
|
||||
placardBj: {
|
||||
width: "210",
|
||||
height: "155",
|
||||
borderRadius: 10,
|
||||
position: "absolute",
|
||||
top: 3,
|
||||
|
||||
},
|
||||
placardTag: {
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: 40,
|
||||
height: 33,
|
||||
},
|
||||
placardInfo: {
|
||||
height: 22,
|
||||
// borderRadius: "0 50px 50px 0",
|
||||
position: "absolute",
|
||||
top: 15,
|
||||
left: 0,
|
||||
backgroundColor: "#f2f2f2",
|
||||
width: 156,
|
||||
color: '#fff',
|
||||
|
||||
// display: flex,
|
||||
// alignItems: "center",
|
||||
// fontSize: 10,
|
||||
// padding-right: 10,
|
||||
},
|
||||
text: {
|
||||
// width: 80,
|
||||
height: 60,
|
||||
color: "#000000",
|
||||
fontSize: 14,
|
||||
},
|
||||
// position: {
|
||||
// position: 'absolute',
|
||||
// bottom: 0,
|
||||
// left: 0,
|
||||
// width: '210px',
|
||||
// height: '26px',
|
||||
// backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
||||
// color: '#fff',
|
||||
// display: 'flex',
|
||||
// alignItems: 'center',
|
||||
// padding: '0 4px',
|
||||
// fontSize: '11px',
|
||||
// borderRadius: '0 0 10px 10px',
|
||||
// },
|
||||
// positionIcon: {
|
||||
// width: '10px',
|
||||
// height: '14px',
|
||||
// marginRight: '4px',
|
||||
// },
|
||||
// positionText: {
|
||||
// flex: 1,
|
||||
// fontFamily: 'microsoft yahei',
|
||||
// },
|
||||
// positionArrow: {
|
||||
// width: '12px',
|
||||
// height: '12px',
|
||||
// }
|
||||
}
|
||||
|
||||
|
||||
console.log(this.widget);
|
||||
const p1 = this.widget.renderToCanvas({
|
||||
wxml,
|
||||
style
|
||||
})
|
||||
console.log("p1", p1);
|
||||
p1.then((res) => {
|
||||
console.log('container', res)
|
||||
this.container = res
|
||||
}).catch(err => {
|
||||
console.log("err", err);
|
||||
})
|
||||
|
||||
const p2 = this.widget.canvasToTempFilePath()
|
||||
p2.then(res => {
|
||||
console.log("res.tempFilePath", res.tempFilePath);
|
||||
this.setData({
|
||||
src: res.tempFilePath,
|
||||
width: 100,
|
||||
height: 100
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
async drawPoster() {
|
||||
const bj = data.attachment?.[0] || data.videos?.[0]?.thumbnail || data.attachment?.[0]
|
||||
return
|
||||
|
||||
const canvas = wx.createOffscreenCanvas({
|
||||
type: '2d',
|
||||
width: 210,
|
||||
height: 168
|
||||
});
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
// 绘制圆角路径
|
||||
const radius = 10;
|
||||
const width = 210;
|
||||
const height = 168;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(radius, 0);
|
||||
ctx.lineTo(width - radius, 0);
|
||||
ctx.quadraticCurveTo(width, 0, width, radius);
|
||||
ctx.lineTo(width, height - radius);
|
||||
ctx.quadraticCurveTo(width, height, width - radius, height);
|
||||
ctx.lineTo(radius, height);
|
||||
ctx.quadraticCurveTo(0, height, 0, height - radius);
|
||||
ctx.lineTo(0, radius);
|
||||
ctx.quadraticCurveTo(0, 0, radius, 0);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
// 绘制背景
|
||||
let bgImg = canvas.createImage();
|
||||
// 等待图片加载
|
||||
const backImg = "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5"
|
||||
await new Promise(resolve => {
|
||||
bgImg.onload = resolve;
|
||||
bgImg.src = backImg; // 要加载的图片 url
|
||||
})
|
||||
console.log(11);
|
||||
ctx.drawImage(bgImg, 0, 10, 210, 158);
|
||||
// 绘制二维码
|
||||
let mycode = canvas.createImage();
|
||||
// 等待图片加载
|
||||
const promoImg = "https://app.gter.net/image/miniApp/HKRenting/high-quality-tag.png"
|
||||
await new Promise(resolve => {
|
||||
mycode.onload = resolve;
|
||||
mycode.src = promoImg; // 要加载的图片 url
|
||||
})
|
||||
|
||||
console.log(12);
|
||||
|
||||
// 创建二维码圆形
|
||||
ctx.save();
|
||||
ctx.drawImage(mycode, 165, -0, 48, 40);
|
||||
ctx.restore();
|
||||
|
||||
// ctx.arc(568, 524, 120, 0, 2 * Math.PI);
|
||||
// ctx.clip();
|
||||
// ctx.fillStyle = "#FFF";
|
||||
// ctx.fill();
|
||||
// ctx.drawImage(mycode, 458, 414, 220, 220);
|
||||
// ctx.restore();
|
||||
|
||||
const imgData = canvas.toDataURL();
|
||||
const time = new Date().getTime();
|
||||
const filePath = wx.env.USER_DATA_PATH + "/poster" + time + "share" + ".png";
|
||||
|
||||
const fs = wx.getFileSystemManager();
|
||||
console.log("imgData", imgData);
|
||||
this.setData({
|
||||
imgData
|
||||
})
|
||||
// wx.getImageInfo({
|
||||
// src: imgData,
|
||||
// success(res) {
|
||||
// console.log(res.width)
|
||||
// console.log(res.height)
|
||||
// }
|
||||
// })
|
||||
// fs.writeFile({
|
||||
// filePath,
|
||||
// data: imgData.replace(/^data:image\/\w+;base64,/, ""),
|
||||
// encoding: 'base64',
|
||||
// success: res => {
|
||||
// console.log("filePath", filePath);
|
||||
// wx.showShareImageMenu({
|
||||
// path: filePath,
|
||||
// success: res => {
|
||||
// console.log(res);
|
||||
// }
|
||||
// })
|
||||
// },
|
||||
// fail: err => {
|
||||
// // 此处可能存在内存满了的情况
|
||||
// // 需要根据具体需求处理
|
||||
// console.log(err);
|
||||
// }
|
||||
// });
|
||||
// fs.close();
|
||||
|
||||
},
|
||||
|
||||
generateShareImage() {
|
||||
|
||||
const screenWidth = this.data.screenWidth
|
||||
console.log("screenWidth", screenWidth);
|
||||
|
||||
// / 创建离屏 2D canvas 实例
|
||||
const canvas = wx.createOffscreenCanvas({
|
||||
type: '2d',
|
||||
width: 300,
|
||||
height: 150
|
||||
})
|
||||
// 获取 context。注意这里必须要与创建时的 type 一致
|
||||
const context = canvas.getContext('2d')
|
||||
|
||||
// 创建一个图片
|
||||
const image = canvas.createImage()
|
||||
// 等待图片加载
|
||||
new Promise(resolve => {
|
||||
image.onload = resolve
|
||||
image.src = '/img/apartment-bottom.png' // 要加载的图片 url
|
||||
})
|
||||
|
||||
// 把图片画到离屏 canvas 上
|
||||
context.clearRect(0, 0, 300, 150)
|
||||
context.drawImage(image, 0, 0, 300, 150)
|
||||
|
||||
// 获取画完后的数据
|
||||
const imgData = context.getImageData(0, 0, 300, 150)
|
||||
console.log("imgData", imgData);
|
||||
|
||||
|
||||
// 获取视图层Canvas
|
||||
const query = wx.createSelectorQuery();
|
||||
query.select('#displayCanvas')
|
||||
.fields({
|
||||
node: true,
|
||||
size: true
|
||||
miucms.generatePoster({
|
||||
bj,
|
||||
title,
|
||||
type: 1,
|
||||
}).then(res => {
|
||||
this.setData({
|
||||
shareImage: res
|
||||
})
|
||||
.exec((res) => {
|
||||
const displayCanvas = res[0].node;
|
||||
const displayCtx = displayCanvas.getContext('2d');
|
||||
|
||||
// 绘制imgData到视图层Canvas
|
||||
displayCtx.putImageData(imgData, 0, 0);
|
||||
|
||||
// 将视图层Canvas内容转换为临时文件路径
|
||||
wx.canvasToTempFilePath({
|
||||
canvas: displayCanvas,
|
||||
success: (res) => {
|
||||
const imagePath = res.tempFilePath;
|
||||
|
||||
// 更新页面数据,显示图片
|
||||
this.setData({
|
||||
imagePath: imagePath
|
||||
});
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('转换失败:', err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// return
|
||||
// wx.createSelectorQuery().select('#cvs1').node(res => {
|
||||
// console.log('select canvas', res)
|
||||
// const ctx1 = res.node.getContext('2d')
|
||||
// res.node.width = screenWidth
|
||||
// res.node.height = screenWidth
|
||||
|
||||
// setInterval(() => {
|
||||
// ctx1.drawImage(video, 0, 0, w * dpr, h * dpr);
|
||||
// }, 1000 / 24)
|
||||
// }).exec()
|
||||
|
||||
console.log("shareImage", this.data.shareImage);
|
||||
})
|
||||
},
|
||||
|
||||
getList() {
|
||||
|
@ -6,7 +6,6 @@
|
||||
"go-login": "../../template/goLogin/goLogin",
|
||||
"report": "../../template/report/report",
|
||||
"head-swiper": "../../template/headSwiper/headSwiper",
|
||||
"nearby-school": "/template/nearbySchool/nearbySchool",
|
||||
"wxml-to-canvas": "wxml-to-canvas"
|
||||
"nearby-school": "/template/nearbySchool/nearbySchool"
|
||||
}
|
||||
}
|
@ -1264,77 +1264,4 @@ map .clickmap {
|
||||
.bottom-bar .bottom-bar-share::after {
|
||||
border: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.placard {
|
||||
width: 210px;
|
||||
height: 158px;
|
||||
position: relative;
|
||||
padding-top: 3px;
|
||||
|
||||
.placard-bj {
|
||||
width: 210px;
|
||||
height: 155px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.placard-tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 33px;
|
||||
}
|
||||
|
||||
.placard-info {
|
||||
height: 22px;
|
||||
border-radius: 0 50px 50px 0;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 0;
|
||||
background-color: rgba(242, 242, 242, 1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
padding-right: 10px;
|
||||
|
||||
.avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
margin-left: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.position {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 210px;
|
||||
height: 26px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
font-size: 11px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
|
||||
.position-icon {
|
||||
width: 10px;
|
||||
height: 14px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.position-text {
|
||||
flex: 1;
|
||||
font-family: 'microsoft yahei';
|
||||
}
|
||||
|
||||
.position-arrow {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
}
|
||||
}
|
@ -4,26 +4,6 @@
|
||||
<header-nav bgcolor="{{ operationsTop ? '#fff' : 'transparent' }}">公寓详情</header-nav>
|
||||
<view class="bj"></view>
|
||||
<view class="content">
|
||||
<wxml-to-canvas class="widget" width="{{ 210 }}" height="{{ 158 }}"></wxml-to-canvas>
|
||||
|
||||
<view class="placard">
|
||||
<image class="placard-bj" src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5" mode="aspectFill"></image>
|
||||
<image class="placard-tag" src="https://app.gter.net/image/miniApp/HKRenting/high-quality-tag.png" mode="widthFix"></image>
|
||||
<view class="placard-info">
|
||||
<image class="avatar" src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d_Zwocn3qqsgFptxhcq_cQnrld6YjDgwVBq_D-81qNDQyOQ~~" mode="widthFix"></image>
|
||||
肖荣豪 为你推荐
|
||||
</view>
|
||||
|
||||
<view class="position">
|
||||
<image class="position-icon" src="https://app.gter.net/image/miniApp/HKRenting/position-icon.svg" mode="widthFix"></image>
|
||||
<view class="position-text">
|
||||
香港 | 多房型 HK$5600起
|
||||
</view>
|
||||
<image class="position-arrow" src="https://app.gter.net/image/miniApp/HKRenting/arrow-round-yellow.svg"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image src="{{ src }}" mode="widthFix"></image>
|
||||
|
||||
<image class="arc" src="/img/arc-shadow.png"></image>
|
||||
<view class="media-module">
|
||||
@ -192,11 +172,11 @@
|
||||
|
||||
<!-- <report uniqid="{{ data.uniqid }}" bindtoReport="toReport" types="apartment"></report> -->
|
||||
|
||||
<cover-view class="around-school-alert" data-show="0" hidden="{{ !showSchool }}">
|
||||
<cover-view class="around-school-alert" data-show="0" hidden="{{ showSchool }}">
|
||||
<cover-view class="inner">
|
||||
<cover-view class="title">
|
||||
<cover-view class="text">房源距离学校</cover-view>
|
||||
<cover-image src="/img/right-close.png" class="close" bindtap="showSchoolAlert" data-show="0"></cover-image>
|
||||
<!-- <cover-image src="/img/.png" class="close" bindtap="showSchoolAlert" data-show="0"></cover-image> -->
|
||||
</cover-view>
|
||||
<cover-view class="list">
|
||||
<cover-view class="item" wx:for="{{ data.pointData }}" wx:key="index">
|
||||
|
@ -2404,67 +2404,3 @@ map .clickmap {
|
||||
border: none;
|
||||
height: 0;
|
||||
}
|
||||
.placard {
|
||||
width: 210px;
|
||||
height: 158px;
|
||||
position: relative;
|
||||
padding-top: 3px;
|
||||
}
|
||||
.placard .placard-bj {
|
||||
width: 210px;
|
||||
height: 155px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.placard .placard-tag {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 40px;
|
||||
height: 33px;
|
||||
}
|
||||
.placard .placard-info {
|
||||
height: 22px;
|
||||
border-radius: 0 50px 50px 0;
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
left: 0;
|
||||
background-color: #f2f2f2;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
.placard .placard-info .avatar {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 50%;
|
||||
margin-left: 2px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.placard .position {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 210px;
|
||||
height: 26px;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
font-size: 11px;
|
||||
border-radius: 0 0 10px 10px;
|
||||
}
|
||||
.placard .position .position-icon {
|
||||
width: 10px;
|
||||
height: 14px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
.placard .position .position-text {
|
||||
flex: 1;
|
||||
font-family: 'microsoft yahei';
|
||||
}
|
||||
.placard .position .position-arrow {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ Page({
|
||||
},
|
||||
|
||||
async drawPoster() {
|
||||
|
||||
return
|
||||
// 修改画布创建方式
|
||||
const canvas = wx.createOffscreenCanvas({
|
||||
type: "2d",
|
||||
@ -26,8 +28,7 @@ Page({
|
||||
let bgImg = canvas.createImage();
|
||||
await new Promise((resolve) => {
|
||||
bgImg.onload = resolve;
|
||||
bgImg.src = "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-c-pstfHzqqsgFptxhT66QUmybYLYnAVBJQe2HpJNYt7VMACPX-Rzrt0ByvH4SjsUfxT00NDI5";
|
||||
// bgImg.src = "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5";
|
||||
bgImg.src = "https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5";
|
||||
});
|
||||
// 计算aspectFill模式参数
|
||||
const containerRatio = 280 / 219; // 容器宽高比 (280x219)
|
||||
@ -67,38 +68,36 @@ Page({
|
||||
|
||||
// 修正缩放逻辑
|
||||
if (imageRatio > containerRatio) {
|
||||
// 图片更宽时,按容器高度缩放
|
||||
drawHeight = 219;
|
||||
drawWidth = drawHeight * imageRatio;
|
||||
offsetX = (280 - drawWidth) / 2;
|
||||
|
||||
// 源图片裁剪:取中间宽度区域
|
||||
srcWidth = bgImg.height * containerRatio;
|
||||
srcX = (bgImg.width - srcWidth) / 2;
|
||||
// 图片更宽时,按容器高度缩放
|
||||
drawHeight = 219;
|
||||
drawWidth = drawHeight * imageRatio;
|
||||
offsetX = (280 - drawWidth) / 2;
|
||||
|
||||
// 源图片裁剪:取中间宽度区域
|
||||
srcWidth = bgImg.height * containerRatio;
|
||||
srcX = (bgImg.width - srcWidth) / 2;
|
||||
} else {
|
||||
// 图片更高时,按容器宽度缩放
|
||||
drawWidth = 280;
|
||||
drawHeight = drawWidth / imageRatio;
|
||||
offsetY = (219 - drawHeight) / 2;
|
||||
|
||||
// 源图片裁剪:取中间高度区域
|
||||
srcHeight = bgImg.width / containerRatio;
|
||||
srcY = (bgImg.height - srcHeight) / 2;
|
||||
// 图片更高时,按容器宽度缩放
|
||||
drawWidth = 280;
|
||||
drawHeight = drawWidth / imageRatio;
|
||||
offsetY = (219 - drawHeight) / 2;
|
||||
|
||||
// 源图片裁剪:取中间高度区域
|
||||
srcHeight = bgImg.width / containerRatio;
|
||||
srcY = (bgImg.height - srcHeight) / 2;
|
||||
}
|
||||
|
||||
console.log("srcX", srcX, "srcY", srcY);
|
||||
|
||||
// 修改后的drawImage调用
|
||||
ctx.drawImage(
|
||||
bgImg,
|
||||
srcX,
|
||||
srcY,
|
||||
srcWidth, // 改为计算后的裁剪宽度
|
||||
srcHeight, // 改为计算后的裁剪高度
|
||||
srcY,
|
||||
srcWidth, // 改为计算后的裁剪宽度
|
||||
srcHeight, // 改为计算后的裁剪高度
|
||||
offsetX,
|
||||
4,
|
||||
drawWidth,
|
||||
drawHeight
|
||||
4,
|
||||
drawWidth,
|
||||
drawHeight
|
||||
);
|
||||
ctx.restore();
|
||||
|
||||
@ -152,9 +151,6 @@ Page({
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// 绘制用户名位置需要同步调整
|
||||
// ctx.fillText(displayText + " 为你推荐", 30, 43); // 保持原位置,实际可根据infoWidth调整
|
||||
|
||||
// 绘制头像
|
||||
let avatarImg = canvas.createImage();
|
||||
await new Promise((resolve) => {
|
||||
@ -172,7 +168,7 @@ Page({
|
||||
ctx.fillStyle = "#000";
|
||||
ctx.font = "14px PingFang SC";
|
||||
// 修改最终文本绘制调用
|
||||
console.log("displayText",displayText);
|
||||
console.log("displayText", displayText);
|
||||
ctx.fillText(displayText, 30, 43); // 使用处理后的文本
|
||||
|
||||
// 绘制底部信息栏
|
||||
@ -288,4 +284,4 @@ Page({
|
||||
imageUrl: this.data.filePath,
|
||||
};
|
||||
},
|
||||
});
|
||||
});
|
@ -1,5 +1,4 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"wxml-to-canvas": "wxml-to-canvas"
|
||||
}
|
||||
}
|
@ -83,4 +83,72 @@
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.placard-no-pic {
|
||||
width: 280px;
|
||||
height: 224px;
|
||||
position: relative;
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #555555;
|
||||
font-size: 14px;
|
||||
margin-bottom: 13px;
|
||||
|
||||
.avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
position: relative;
|
||||
|
||||
.bj {
|
||||
width: 280px;
|
||||
height: 188px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.text {
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
font-family: 'PingFangSC-Regular', 'PingFang SC', sans-serif;
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
.position {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 44px;
|
||||
}
|
||||
|
||||
|
||||
.type {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 44px;
|
||||
}
|
||||
|
||||
.price {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.sum {
|
||||
font-family: 'PingFangSC-Semibold', 'PingFang SC Semibold', 'PingFang SC', sans-serif;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
color: #FA6B11;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,21 +1,44 @@
|
||||
<!--pages/dome/dome.wxml-->
|
||||
<view class="container">
|
||||
<view class="placard-no-pic">
|
||||
<view class="info">
|
||||
<image class="avatar"
|
||||
src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d_Zwocn3qqsgFptxhcq_cQnrld6YjDgwVBq_D-81qNDQyOQ~~"
|
||||
mode="widthFix"></image>
|
||||
肖荣豪 为你推荐
|
||||
</view>
|
||||
<view class="box">
|
||||
<image class="bj" src="https://app.gter.net/image/miniApp/HKRenting/share-default-bj.png" mode="widthFix">
|
||||
</image>
|
||||
<view class="position text">香港 > 新界 > 大围/沙田</view>
|
||||
<view class="type text">合租 · 房间</view>
|
||||
<view class="price text"><view class="sum">5300</view> HK$/月</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<image class="image" src="{{ src }}" mode="widthFix"></image>
|
||||
23
|
||||
<view class="placard">
|
||||
<image class="placard-bj" src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5" mode="aspectFill"></image>
|
||||
<image class="placard-tag" src="https://app.gter.net/image/miniApp/HKRenting/high-quality-tag.png" mode="widthFix"></image>
|
||||
<image class="placard-bj"
|
||||
src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-T9pwkfHvqqsgFptxhXa6QWi2uePJ5Bg8cFLPIqoYV7MsLCmeW5lr_-kU8uRQ0NDI5"
|
||||
mode="aspectFill"></image>
|
||||
<image class="placard-tag" src="https://app.gter.net/image/miniApp/HKRenting/high-quality-tag.png"
|
||||
mode="widthFix"></image>
|
||||
<view class="placard-info">
|
||||
<image class="avatar" src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d_Zwocn3qqsgFptxhcq_cQnrld6YjDgwVBq_D-81qNDQyOQ~~" mode="widthFix"></image>
|
||||
<image class="avatar"
|
||||
src="https://oss.x-php.com/Zvt57TuJSUvkyhw-xG7Y2l-d_Zwocn3qqsgFptxhcq_cQnrld6YjDgwVBq_D-81qNDQyOQ~~"
|
||||
mode="widthFix"></image>
|
||||
肖荣豪 为你推荐
|
||||
</view>
|
||||
|
||||
<view class="position">
|
||||
<image class="position-icon" src="https://app.gter.net/image/miniApp/HKRenting/position-icon.svg" mode="widthFix"></image>
|
||||
<image class="position-icon" src="https://app.gter.net/image/miniApp/HKRenting/position-icon.svg"
|
||||
mode="widthFix"></image>
|
||||
<view class="position-text">
|
||||
香港 | 多房型 HK$5600起
|
||||
</view>
|
||||
<image class="position-arrow" src="https://app.gter.net/image/miniApp/HKRenting/arrow-round-yellow.svg"></image>
|
||||
<image class="position-arrow" src="https://app.gter.net/image/miniApp/HKRenting/arrow-round-yellow.svg">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
@ -71,3 +71,59 @@
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.placard-no-pic {
|
||||
width: 280px;
|
||||
height: 224px;
|
||||
position: relative;
|
||||
}
|
||||
.placard-no-pic .info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #555555;
|
||||
font-size: 14px;
|
||||
margin-bottom: 13px;
|
||||
}
|
||||
.placard-no-pic .info .avatar {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.placard-no-pic .box {
|
||||
position: relative;
|
||||
}
|
||||
.placard-no-pic .box .bj {
|
||||
width: 280px;
|
||||
height: 188px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.placard-no-pic .box .text {
|
||||
font-size: 16px;
|
||||
color: #000000;
|
||||
font-family: 'PingFangSC-Regular', 'PingFang SC', sans-serif;
|
||||
line-height: 50px;
|
||||
}
|
||||
.placard-no-pic .box .position {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 44px;
|
||||
}
|
||||
.placard-no-pic .box .type {
|
||||
position: absolute;
|
||||
top: 50px;
|
||||
left: 44px;
|
||||
}
|
||||
.placard-no-pic .box .price {
|
||||
position: absolute;
|
||||
top: 100px;
|
||||
left: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.placard-no-pic .box .price .sum {
|
||||
font-family: 'PingFangSC-Semibold', 'PingFang SC Semibold', 'PingFang SC', sans-serif;
|
||||
font-weight: 650;
|
||||
font-size: 20px;
|
||||
color: #FA6B11;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ Page({
|
||||
// 来自页面内转发按钮
|
||||
if (res.from === 'button') var types = res.from === 'button' ? 'share_btn' : 'show';
|
||||
let that = this;
|
||||
console.log("res", that.data.info.share_img);
|
||||
// console.log("res", that.data.info.share_img);
|
||||
const info = this.data.info
|
||||
|
||||
// let title = `中国香港 > ${ info.locationList[0].head } > ${ info.locationList[0].end }`
|
||||
@ -243,7 +243,7 @@ Page({
|
||||
else imageUrl = that.data.info.share_img
|
||||
return {
|
||||
title,
|
||||
imageUrl,
|
||||
imageUrl: this.data.shareImage,
|
||||
success: function (res) {
|
||||
miucms.share(app, types)
|
||||
},
|
||||
@ -259,7 +259,7 @@ Page({
|
||||
return {
|
||||
// title: this.data.info.subject,
|
||||
title,
|
||||
imageUrl,
|
||||
imageUrl: this.data.shareImage,
|
||||
}
|
||||
},
|
||||
get_content: function () {
|
||||
@ -362,6 +362,8 @@ Page({
|
||||
|
||||
this.handleDetailData()
|
||||
|
||||
if (data.info.intermediary != 6) this.drawPoster()
|
||||
|
||||
// 判断是否需要获取附近房源
|
||||
if (data.info.latitude && data.info.verified == 0 && data.info.intermediary != 6 && data.isintermediary != 1) this.getNearbListings()
|
||||
else this.nearbListingsState = true // 阻止上拉底部加载的
|
||||
@ -380,6 +382,51 @@ Page({
|
||||
|
||||
},
|
||||
|
||||
drawPoster() {
|
||||
console.log("444");
|
||||
let obj = {}
|
||||
const info = this.data.info
|
||||
console.log("intermediary", info.intermediary);
|
||||
const image = info.picturegroup || []
|
||||
const isintermediary = this.data.isintermediary || 0 // 是否是认证中介
|
||||
console.log("isintermediary", isintermediary);
|
||||
let title = `${ info.gptype }·${ info.type }·HK$${ info.rent }`
|
||||
obj['bj'] = image?.[0]?.thumbnail || ''
|
||||
obj['title'] = title
|
||||
// console.log("image", image);
|
||||
if (isintermediary == 1) { // 认证中介
|
||||
obj['type'] = 2
|
||||
} else if (info.intermediary == 1) { // 普通中介
|
||||
obj['type'] = 3
|
||||
} else if (info.intermediary == 3 && info.verified == 0) { // 普通个人房源
|
||||
obj['type'] = 4
|
||||
} else if (info.intermediary == 3 && info.verified == 1) { // 认证个人房源
|
||||
obj['type'] = 5
|
||||
} else if (info.intermediary == 4) { // 招室友
|
||||
obj['type'] = 6
|
||||
} else if (info.intermediary == 5) { // 其他
|
||||
obj['type'] = 7
|
||||
}
|
||||
|
||||
|
||||
console.log("obj", obj);
|
||||
let res = null
|
||||
if (obj['bj']) res = miucms.generatePoster(obj)
|
||||
else {
|
||||
console.log(5555);
|
||||
obj['position'] = `香港 > ${ info.locationList[0].head } > ${ info.locationList[0].end }`
|
||||
obj['typeText'] = `${ info.gptype } · ${ info.type }`
|
||||
obj['price'] = info.rent
|
||||
res = miucms.generatePosterNoImage(obj)
|
||||
}
|
||||
console.log("res", res);
|
||||
res.then(res => {
|
||||
this.setData({
|
||||
shareImage: res
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
// 获取语言包 保存全局
|
||||
getDtailsLangs() {
|
||||
miucms.request(`${app.globalData.baseURL}/tenement/v2/api/details/langs`).then(res => {
|
||||
|
@ -75,6 +75,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- <image wx:if="{{ shareImage }}" class="shareImage" src="{{ shareImage }}"mode="widthFix"></image> -->
|
||||
|
||||
<!-- 房源详细信息 -->
|
||||
<view class="HResource-detail">
|
||||
<view class="HResource-header">
|
||||
@ -241,7 +243,7 @@
|
||||
<view class="other-information else-box" wx:if="{{ attestationElseResource.length != 0 }}">
|
||||
<view class="other-information-name flexacenter">发布者的其他{{ info['intermediary'] == 1 ? '' : '认证' }}房源<view class="else-quantity">({{ attestationElseResource.length }})</view>
|
||||
</view>
|
||||
<block wx:for="{{ attestationElseResource }}">
|
||||
<block wx:for="{{ attestationElseResource }}" wx:key="index">
|
||||
<common-list item="{{ item }}"></common-list>
|
||||
</block>
|
||||
</view>
|
||||
|
@ -0,0 +1,4 @@
|
||||
.shareImage {
|
||||
width: 280px;
|
||||
height: 224px;
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
.shareImage {
|
||||
width: 280px;
|
||||
height: 224px;
|
||||
}
|
950
utils/miucms.js
950
utils/miucms.js
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user