Skip to content

Commit

Permalink
feat(h5 component): 支持canvasgetimagedata canvasputimage api
Browse files Browse the repository at this point in the history
  • Loading branch information
Littly committed Apr 2, 2019
1 parent 3764452 commit 17d804d
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 69 deletions.
47 changes: 20 additions & 27 deletions packages/taro-components/src/components/canvas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,20 @@ export default class Canvas extends Nerv.Component {
bindError: null
}
state = {
width: null,
height: null
width: 300,
height: 150
}
getRef = ref => {
getWrapRef = ref => {
const dom = findDOMNode(ref)
this.canvasDom = dom
this.wrapDom = dom
}
shouldComponentUpdate (nProps, nState) {
if (nState.width !== this.state.width
|| nState.height !== this.state.height) {
return true
}
return false
getCanvasRef = ref => {
const dom = findDOMNode(ref)
this.canvasDom = dom
}
componentDidMount () {
if (!this.canvasDom) return
const { width, height } = this.canvasDom.getBoundingClientRect()
if (!this.wrapDom) return
const { width, height } = this.wrapDom.getBoundingClientRect()
this.setState({
width,
height
Expand All @@ -47,27 +44,23 @@ export default class Canvas extends Nerv.Component {
render () {
const { canvasId, onTouchStart, onTouchMove, onTouchEnd, onTouchCancel, className } = this.props
const { width, height } = this.state
const props = {
const wrapProps = {
className: classnames('taro-canvas', className),
ref: this.getWrapRef
}
const canvasProps = {
canvasId,
onTouchStart,
onTouchMove,
onTouchEnd,
onTouchCancel
}
if (isNumber(width)) {
props.width = width
}
if (isNumber(height)) {
props.height = height
onTouchCancel,
width,
height,
ref: this.canvasRef
}
return (
<div
className={classnames(
'taro-canvas',
className
)}
ref={this.getRef}>
<canvas {...props} />
<div {...wrapProps}>
<canvas {...canvasProps} />
</div>
)
}
Expand Down
18 changes: 9 additions & 9 deletions packages/taro-components/src/utils/touchable.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,27 @@ const touchable = (opt = {
}
timer = null

onTouchStart = () => {
onTouchStart = e => {
const { bindTouchStart, bindLongTap } = this.props
bindTouchStart && bindTouchStart()
bindTouchStart && bindTouchStart(e)
this.timer = setTimeout(() => {
bindLongTap && bindLongTap()
bindLongTap && bindLongTap(e)
}, opt.longTapTime)
}
onTouchMove = () => {
onTouchMove = e => {
this.timer && clearTimeout(this.timer)
const { bindTouchMove } = this.props
bindTouchMove && bindTouchMove()
bindTouchMove && bindTouchMove(e)
}
onTouchEnd = () => {
onTouchEnd = e => {
this.timer && clearTimeout(this.timer)
const { bindTouchEnd } = this.props
bindTouchEnd && bindTouchEnd()
bindTouchEnd && bindTouchEnd(e)
}
onTouchCancel = () => {
onTouchCancel = e => {
this.timer && clearTimeout(this.timer)
const { bindTouchCancel } = this.props
bindTouchCancel && bindTouchCancel()
bindTouchCancel && bindTouchCancel(e)
}
render () {
const props = {
Expand Down
30 changes: 30 additions & 0 deletions packages/taro-h5/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module.exports = {
extends: ['standard', 'standard-jsx'],
env: {
browser: true,
node: true,
es6: true,
jest: true
},
globals: {
wx: true,
my: true,
swan: true,
tt: true,
getApp: true,
__wxRoute: true,
getCurrentPages: true,
requirePlugin: true
},
rules: {
'no-unused-expressions': 0,
'no-useless-constructor': 0,
'no-undef': 2
},
settings: {
react: {
pragma: 'Nerv'
}
},
parser: 'babel-eslint'
}
3 changes: 3 additions & 0 deletions packages/taro-h5/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@
"@tarojs/taro": "1.2.22",
"babel-core": "7.0.0-bridge.0",
"babel-jest": "24.4.0",
"eslint": "^4.18.2",
"eslint-config-standard": "^11.0.0",
"eslint-config-standard-jsx": "^4.0.2",
"jest": "24.0.0",
"jest-fetch-mock": "^1.6.5",
"jest-localstorage-mock": "^2.2.0",
Expand Down
47 changes: 46 additions & 1 deletion packages/taro-h5/src/api/canvas/canvasGetImageData.js
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
export default () => {}
import { findDOMNode } from 'nervjs'

/**
* @typedef {Object} Param
* @property {String} canvasId 画布标识,传入 <canvas> 组件的 canvas-id 属性。
* @property {Number} x 将要被提取的图像数据矩形区域的左上角横坐标
* @property {Number} y 将要被提取的图像数据矩形区域的左上角纵坐标
* @property {Number} width 将要被提取的图像数据矩形区域的宽度
* @property {Number} height 将要被提取的图像数据矩形区域的高度
* @property {Function} [success] 接口调用成功的回调函数
* @property {Function} [fail] 接口调用失败的回调函数
* @property {Function} [complete] 接口调用结束的回调函数(调用成功、失败都会执行)
*/

/**
* 获取 canvas 区域隐含的像素数据。
* @param {Param} object 参数
* @param {Object} componentInstance
*/
const canvasGetImageData = ({ canvasId, success, fail, complete, x, y, width, height }, componentInstance=document.body) => {
const dom = findDOMNode(componentInstance)

/** @type {HTMLCanvasElement} */
const canvas = dom.querySelector(`[canvasId=${canvasId}]`);
try {
const ctx = canvas.getContext('2d')
const data = ctx.getImageData(x, y, width, height)
const res = {
width,
height,
data
}
success && success(res)
complete && complete()
return Promise.resolve(res)
} catch (e) {
const res = {
errMsg: e.message
}
fail && fail(res)
complete && complete()
return Promise.reject(res)
}
}

export default canvasGetImageData
47 changes: 46 additions & 1 deletion packages/taro-h5/src/api/canvas/canvasPutImageData.js
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
export default () => {}
import { findDOMNode } from 'nervjs'

/**
* @typedef {Object} Param
* @property {String} canvasId 是 画布标识,传入 <canvas> 组件的 canvas-id 属性。
* @property {Uint8ClampedArray} data 是 图像像素点数据,一维数组,每四项表示一个像素点的 rgba
* @property {Number} x 是 源图像数据在目标画布中的位置偏移量(x 轴方向的偏移量)
* @property {Number} y 是 源图像数据在目标画布中的位置偏移量(y 轴方向的偏移量)
* @property {Number} width 是 源图像数据矩形区域的宽度
* @property {Number} height 是 源图像数据矩形区域的高度
* @property {Function} [success] 接口调用成功的回调函数
* @property {Function} [fail] 接口调用失败的回调函数
* @property {Function} [complete] 接口调用结束的回调函数(调用成功、失败都会执行)
*/

/**
* 将像素数据绘制到画布。在自定义组件下,第二个参数传入自定义组件实例 this,以操作组件内 <canvas> 组件
* @param {Param} object 参数
* @param {Object} componentInstance 在自定义组件下,当前组件实例的this,以操作组件内 <canvas> 组件
*/
const canvasPutImageData = ({ canvasId, data, x, y, success, fail, complete }, componentInstance=document.body) => {
const dom = findDOMNode(componentInstance)

/** @type {HTMLCanvasElement} */
const canvas = dom.querySelector(`[canvasId=${canvasId}]`);
try {
const ctx = canvas.getContext('2d')

ctx.putImageData(data, x, y)
const res = {
errMsg: 'canvasPutImageData:ok'
}
success && success(res)
complete && complete()
return Promise.resolve(res)
} catch (e) {
const res = {
errMsg: e.message
}
fail && fail(res)
complete && complete()
return Promise.reject(res)
}
}

export default canvasPutImageData
28 changes: 16 additions & 12 deletions packages/taro-h5/src/api/canvas/canvasToTempFilePath.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { findDOMNode } from 'nervjs'

/**
* @typedef {Object} Param
* @property {Number} [x] 指定的画布区域的左上角横坐标,默认值 0
Expand All @@ -17,32 +18,35 @@ import { findDOMNode } from 'nervjs'

/**
* 把当前画布指定区域的内容导出生成指定大小的图片。在 draw() 回调里调用该方法才能保证图片导出成功。
* @param {Param} opt 参数
* @param {Param} object 参数
* @param {Object} componentInstance 在自定义组件下,当前组件实例的this,以操作组件内 <canvas> 组件
* @todo 暂未支持尺寸相关功能
*/

const canvasToTempFilePath = ({ canvasId, fileType, success, fail, complete }, componentInstance) => {
const canvasToTempFilePath = ({ canvasId, fileType, quality, success, fail, complete }, componentInstance=document.body) => {
const dom = findDOMNode(componentInstance)

/** @type {HTMLCanvasElement} */
const canvas = dom.querySelector(`[canvasId=${canvasId}]`);

try {
// /** @type {CanvasRenderingContext2D} */
const dataURL = canvas.toDataURL(`image/${fileType || 'png'}`, opt.quality)
const dataURL = canvas.toDataURL(`image/${fileType || 'png'}`, quality)
const res = {
tempFilePath: dataURL,
res: 'canvasToTempFilePath:ok'
}

success && success({
tempFilePath: dataURL
})
success && success(res)
complete && complete()
return Promise.resolve(res)
} catch (e) {
fail && fail({
const res = {
errMsg: e.message
})
}
fail && fail(res)
complete && complete()
return Promise.reject(res)
}
complete && complete({
res: 'canvasToTempFilePath:ok'
})

}

Expand Down
45 changes: 26 additions & 19 deletions packages/taro-h5/src/api/canvas/createCanvasContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { isFunction } from '../utils/index'
* @param {string} canvasId 要获取上下文的 <canvas> 组件 canvas-id 属性
* @param {Object} componentInstance 在自定义组件下,当前组件实例的this,表示在这个自定义组件下查找拥有 canvas-id 的 <canvas> ,如果省略则不在任何自定义组件内查找
*/
const createCanvasContext = (canvasId, componentInstance) => {
const createCanvasContext = (canvasId, componentInstance=document.body) => {
const dom = findDOMNode(componentInstance)
/** @type {HTMLCanvasElement} */
const canvas = dom.querySelector(`[canvasId=${canvasId}]`);
Expand Down Expand Up @@ -43,21 +43,28 @@ const createCanvasContext = (canvasId, componentInstance) => {
* 即 reserve 参数为 false,则在本次调用绘制之前 native 层会先清空画布再继续绘制;
* 若 reserve 参数为 true,则保留当前画布上的内容,本次调用 drawCanvas 绘制的内容覆盖在上面,
* 默认 false。
* @param {Function} callback 绘制完成后执行的回调函数
* @param {Function} [callback] 绘制完成后执行的回调函数
* @todo 每次draw都会读取width和height
*/
const draw = (reserve = false, callback) => {
if (!reserve) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
try {
if (!reserve) {
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
actions.forEach(({func, args}) => {
func.apply(ctx, args)
})
emptyActions()
callback && callback()
return Promise.resolve()
} catch (e) {
return Promise.reject({
errMsg: e.message
})
}
actions.forEach(({func, args}) => {
func.apply(ctx, args)
})
emptyActions()
callback && callback()
}

const CanvasContext = new Proxy({
const customProperties = {
/**
* 设置填充色。
* @param {String} color 填充的颜色,默认颜色为 black。
Expand Down Expand Up @@ -150,27 +157,27 @@ const createCanvasContext = (canvasId, componentInstance) => {
setTextBaseline (textBaseline) {
ctx.textBaseline = textBaseline
}
}, {
}

const CanvasContext = new Proxy(ctx, {
get (target, p) {
if (p === 'draw') return draw

if (isFunction(target[p])) {
return enqueueActions(target[p])
} else if (isFunction(ctx[p])) {
return enqueueActions(ctx[p])
const value = customProperties[p] || target[p]
if (isFunction(value)) {
return enqueueActions(value)
} else {
return p in target
? target[p]
: ctx[p]
return value
}
},
set (target, p, value) {
enqueueActions(() => {
ctx[p] = value
target[p] = value
})()
return true
}
})

return CanvasContext
}

Expand Down

0 comments on commit 17d804d

Please sign in to comment.