Comments (36)
demo运行有问题吗,如果没问题,坐标问题看看页面中canvas所在父容器css样式对不对。
ios无法签名是个什么情况?有稳定的复现步骤吗,操作系统版本呢?
from canvas-draw.
from canvas-draw.
图1234对应的是css样式吗?你贴的是父容器的css样式吗?如果是,那我觉得这些css与问题无关。
是这样的,如果确认demo在你的手机上运行正常,而你写的代码部分ios手机无法签名(需要刷新),建议耐心debug下canvas画布的touchstart、touchmove事件,看看有无异常。或者留下你qq,我加你好友
from canvas-draw.
en能动态的修改canvas的背景图吗?
from canvas-draw.
from canvas-draw.
@Louiszhai @LCtenacity
你好,我也遇到这个问题,只有刷新画布才能签名,你们有解决的方法吗?
谢谢!
from canvas-draw.
from canvas-draw.
@Louiszhai
直接复制的demo代码如下:
// 页面Page1.vue
<div>
<div id="canvasBox" :style="getHorizontalStyle">
<div class="greet">
<span>{{msg}}</span>
<input type="button" value="清屏" @touchstart="clear" @mousedown="clear"/>
<!-- <input type="button" value="生成png图片" @touchstart="savePNG" @mousedown="savePNG"/> -->
</div>
<canvas></canvas>
</div>
<!-- <div class="image-box" v-show="showBox">
<header>
请长按图片并保存至本地后发送好友
<input type="button" value="返回" @click="showBox = false"/>
</header>
<img :src="signImage">
</div> -->
</div>
需要跳转到Page1.vue的代码:
this.$router.push({name: 'page1'})
类似上面的代码,当采用上面的代码跳转到Page1.vue的时候,在画布上移动指针,发现画布没有任何变化,没有画上,然后刷新Page1对应的页面,重新移动指针,发现画布上可以画上对应的签名。
debug对应的draw.js发现touchmove
触发了啊!
另外发现,这两个进入页面的方式唯一的不同是,如果是直接刷新页面进去的,发现console里面有这一段日志:
vue.esm.js:591 [Vue warn]: Do not use built-in or reserved HTML elements as component id: canvas
,如果是直接通过this.$router.push({name: 'page1'})
进去的就不会出现上面的log, 然后画布就不能画签名,奇怪啊!
from canvas-draw.
什么情况下需要刷新画布,麻烦提供尽可能详细的背景信息 Walter Hu [email protected]于2018年11月29日 周四21:18写道:
…
@Louiszhai https://github.com/Louiszhai @LCtenacity https://github.com/LCtenacity 你好,我也遇到这个问题,只有刷新画布才能签名,你们有解决的方法吗? 谢谢! — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub <#5 (comment)>, or mute the thread https://github.com/notifications/unsubscribe-auth/AIBcjeXYgGuUd4_4jPYT6V3yNUZAci0kks5uz96jgaJpZM4T6Uqs .
刷新画布是因为发现直接通过this.$router进去没有发现,只能刷新下页面,发现此时画布可以正常签名了。
@Louiszhai 你好,麻烦能帮忙看下吗?我感觉大部分都会遇到这个问题,我的QQ是: [email protected], 谢谢了!
from canvas-draw.
from canvas-draw.
跟你的demo的代码一样的,我贴下所有的代码:
唯一的不同的地方就是将你的draw.js 采用eslint格式化了,感觉是组件那里加载不对导致的,可是也没有发现哪里加载出问题了?只有刷新这个页面,画布上才能签名。
vue版本是: 2.5.2
<template>
<div class="container">
<div id="canvasBox" :style="getHorizontalStyle" v-show="!showBox">
<div class="greet">
<span>{{msg}}</span>
<input type="button" value="清屏" @touchstart="clear" @mousedown="clear"/>
<input type="button" value="生成png图片" @touchstart="savePNG" @mousedown="savePNG"/>
</div>
<canvas></canvas>
</div>
<div class="image-box" v-show="showBox">
<header>
请长按图片并保存至本地后发送好友
<input type="button" value="返回" @click="showBox = false"/>
</header>
<img :src="signImage">
</div>
</div>
</template>
<script>
import Draw from '../components/draw'
export default {
name: 'canvas',
data () {
return {
msg: '请在下方空白处签名',
degree: 90,
signImage: null,
showBox: false
}
},
components: {
Draw
},
beforeCreate () {
document.title = '手写签名'
},
mounted () {
this.$store.dispatch('UpdateShowHeader', true)
this.$store.dispatch('UpdateShowTabbar', false)
this.canvasBox = document.getElementById('canvasBox')
this.initCanvas()
},
computed: {
getHorizontalStyle () {
const d = document
const w = window.innerWidth || d.documentElement.clientWidth || d.body.clientWidth
const h = window.innerHeight || d.documentElement.clientHeight || d.body.clientHeight
let length = (h - w) / 2
let width = w
let height = h
switch (this.degree) {
case -90:
length = -length
/* eslint no-fallthrough: "error" */
case 90:
width = h
height = w
break
default:
length = 0
}
if (this.canvasBox) {
this.canvasBox.removeChild(document.querySelector('canvas'))
this.canvasBox.appendChild(document.createElement('canvas'))
/* eslint vue/no-async-in-computed-properties: "off" */
setTimeout(() => {
this.initCanvas()
}, 200)
}
return {
transform: `rotate(${this.degree}deg) translate(${length}px,${length}px)`,
width: `${width}px`,
height: `${height}px`,
transformOrigin: 'center center'
}
}
},
methods: {
initCanvas () {
const canvas = document.querySelector('canvas')
this.draw = new Draw(canvas, -this.degree)
},
clear () {
this.draw.clear()
},
download () {
this.draw.downloadPNGImage(this.draw.getPNGImage())
},
savePNG () {
this.signImage = this.draw.getPNGImage()
this.showBox = true
},
upload () {
const image = this.draw.getPNGImage()
const blob = this.draw.dataURLtoBlob(image)
const url = ''
const successCallback = (response) => {
console.log(response)
}
const failureCallback = (error) => {
console.log(error)
}
this.draw.upload(blob, url, successCallback, failureCallback)
}
}
}
</script>
<style>
.container {
width: 100%;
height: 100%;
}
#canvasBox {
display: flex;
flex-direction: column;
height: 100%;
}
.greet {
padding: 20px;
font-size: 20px;
user-select: none;
}
input {
font-size: 20px;
}
.greet select {
font-size: 18px;
}
canvas {
flex: 1;
cursor: crosshair;
border:2px dashed lightgray;
}
.image-box {
width: 100%;
height: 100%;
}
.image-box header{
font-size: 18px;
}
.image-box img {
max-width: 80%;
max-height: 80%;
margin-top: 50px;
border: 1px solid gray;
}
</style>
下面的是draw.js
的代码:
/**
* Created by louizhai on 17/6/30.
* description: Use canvas to draw.
*/
function Draw (canvas, degree, config = {}) {
if (!(this instanceof Draw)) {
return new Draw(canvas, config)
}
if (!canvas) {
return
}
let { width, height } = window.getComputedStyle(canvas, null)
width = width.replace('px', '')
height = height.replace('px', '')
this.canvas = canvas
this.context = canvas.getContext('2d')
this.width = width
this.height = height
const context = this.context
// 根据设备像素比优化canvas绘图
const devicePixelRatio = window.devicePixelRatio
if (devicePixelRatio) {
canvas.style.width = `${width}px`
canvas.style.height = `${height}px`
canvas.height = height * devicePixelRatio
canvas.width = width * devicePixelRatio
context.scale(devicePixelRatio, devicePixelRatio)
} else {
canvas.width = width
canvas.height = height
}
context.lineWidth = 6
context.strokeStyle = 'black'
context.lineCap = 'round'
context.lineJoin = 'round'
Object.assign(context, config)
const { left, top } = canvas.getBoundingClientRect()
const point = {}
const isMobile = /phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone/i.test(navigator.userAgent)
// 移动端性能太弱, 去掉模糊以提高手写渲染速度
if (!isMobile) {
context.shadowBlur = 1
context.shadowColor = 'black'
}
let pressed = false
const paint = (signal) => {
switch (signal) {
case 1:
context.beginPath()
context.moveTo(point.x, point.y)
/* eslint no-fallthrough: "error" */
case 2:
context.lineTo(point.x, point.y)
context.stroke()
break
default:
}
}
const create = signal => (e) => {
e.preventDefault()
if (signal === 1) {
pressed = true
}
if (signal === 1 || pressed) {
e = isMobile ? e.touches[0] : e
point.x = e.clientX - left
point.y = e.clientY - top
paint(signal)
}
}
const start = create(1)
const move = create(2)
const requestAnimationFrame = window.requestAnimationFrame
const optimizedMove = requestAnimationFrame ? (e) => {
requestAnimationFrame(() => {
move(e)
})
} : move
if (isMobile) {
canvas.addEventListener('touchstart', start)
canvas.addEventListener('touchmove', optimizedMove)
} else {
canvas.addEventListener('mousedown', start)
canvas.addEventListener('mousemove', optimizedMove);
['mouseup', 'mouseleave'].forEach((event) => {
canvas.addEventListener(event, () => {
pressed = false
})
})
}
// 重置画布坐标系
if (typeof degree === 'number') {
this.degree = degree
context.rotate((degree * Math.PI) / 180)
switch (degree) {
case -90:
context.translate(-height, 0)
break
case 90:
context.translate(0, -width)
break
case -180:
case 180:
context.translate(-width, -height)
break
default:
}
}
}
Draw.prototype = {
scale (width, height, canvas = this.canvas) {
const w = canvas.width
const h = canvas.height
width = width || w
height = height || h
if (width !== w || height !== h) {
const tmpCanvas = document.createElement('canvas')
const tmpContext = tmpCanvas.getContext('2d')
tmpCanvas.width = width
tmpCanvas.height = height
tmpContext.drawImage(canvas, 0, 0, w, h, 0, 0, width, height)
canvas = tmpCanvas
}
return canvas
},
rotate (degree, image = this.canvas) {
degree = ~~degree
if (degree !== 0) {
const maxDegree = 180
const minDegree = -90
if (degree > maxDegree) {
degree = maxDegree
} else if (degree < minDegree) {
degree = minDegree
}
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
const height = image.height
const width = image.width
const degreePI = (degree * Math.PI) / 180
switch (degree) {
// 逆时针旋转90°
case -90:
canvas.width = height
canvas.height = width
context.rotate(degreePI)
context.drawImage(image, -width, 0)
break
// 顺时针旋转90°
case 90:
canvas.width = height
canvas.height = width
context.rotate(degreePI)
context.drawImage(image, 0, -height)
break
// 顺时针旋转180°
case 180:
canvas.width = width
canvas.height = height
context.rotate(degreePI)
context.drawImage(image, -width, -height)
break
default:
}
image = canvas
}
return image
},
getPNGImage (canvas = this.canvas) {
return canvas.toDataURL('image/png')
},
getJPGImage (canvas = this.canvas) {
return canvas.toDataURL('image/jpeg', 0.5)
},
downloadPNGImage (image) {
const url = image.replace('image/png', 'image/octet-stream;Content-Disposition:attachment;filename=test.png')
window.location.href = url
},
dataURLtoBlob (dataURL) {
const arr = dataURL.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bStr = atob(arr[1])
let n = bStr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bStr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
},
clear () {
let width
let height
switch (this.degree) {
case -90:
case 90:
width = this.height
height = this.width
break
default:
width = this.width
height = this.height
}
this.context.clearRect(0, 0, width, height)
},
upload (blob, url, success, failure) {
const formData = new FormData()
const xhr = new XMLHttpRequest()
xhr.withCredentials = true
formData.append('image', blob, 'sign')
xhr.open('POST', url, true)
xhr.onload = () => {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
success(xhr.responseText)
} else {
failure()
}
}
xhr.onerror = (e) => {
if (typeof failure === 'function') {
failure(e)
} else {
console.log(`upload img error: ${e}`)
}
}
xhr.send(formData)
}
}
export default Draw
from canvas-draw.
感谢反馈,晚点我在iphone手机上测试下
from canvas-draw.
请问你的手机是什么版本?
from canvas-draw.
谢谢作者 @Louiszhai , 是直接在chrome的开发工具上测试的,没有手机测试,我等会给你手机测试的结果。
Edit: 刚才在我的手机IPhone 6 Plus上测试也是一样,画布不起作用,不能签名。 这个应该跟设备没有太大关系,直接使用chrome的模拟器就可以测试。 真不太明白是哪里没有加载出现这个问题的?
刷新就可以在画布上面签名了。。。。
from canvas-draw.
@Louiszhai ,我看到你的这个项目已经有一年没有更新了,还能看到你的回复真的挺意外的。不晓得你对于这个问题还有印象没? 这个问题我感觉大部分人使用可能都会遇到这个问题,需要刷新下页面画布才能签名。否则画布不起作用。。。。
from canvas-draw.
稍等,我刚空下来,在看
from canvas-draw.
@Louiszhai , 多谢!
from canvas-draw.
首先,你的代码是完全能运行的,还没有发现无法签名的情况,从其他路由跳过来也能正常签名。
唯一的不同在于,以下代码我本地没有store,因此注释了
请确认下,是否还有其他问题?
from canvas-draw.
另外,补充下,项目代码拉下来默认存在两个路由/
、/sign
,两个路由之前的跳转也不会导致出现无法签名的情况,猜你可能是别的原因。
from canvas-draw.
刚又测试了,使用this.$router.push({name: 'page1'})
跳转至你粘贴的代码生成的路由,可以直接签名。
我使用chrome直接测试。
from canvas-draw.
@Louiszhai 嗯? 这个奇了怪了,我重新检查下现在。。。。 谢谢
from canvas-draw.
我在项目中也是使用的这段代码,实际上,项目中的场景要更加复杂。
当时项目中工作流如下:
- 上个页面按下按钮,唤出签名页面
- 签名页旋转90°加载(因为app强制不横屏),并正常签名
- 签名后,直接进入预览页,然后才是提交到服务器
跟你的操作基本一致,目前线上没有收到无法签名反馈。
应该是兼容到iphone低版本的,以及Android 4.0左右
希望能给你带来信心~
from canvas-draw.
@Louiszhai 有什么办法debug这个问题吗?
我现在实在没有发现什么特别的代码或者组件没有加载,但是看这个问题应该是哪里没有加载?所以刷新页面就可以签名成功了,代码都是一样的啊,移植到自己的项目跳转到页面就是不能签名,需要刷新才能签名。
from canvas-draw.
方便将你的项目剥离出一个简单的无法签名版本吗?
我尝试运行看看
from canvas-draw.
还有一个问题就是发现如果签名的页面存在顶部返回按钮,这个时候的签名的笔画下来的是有一点的偏移的,这个是在这段代码里面的,这个应该怎么计算位置让画笔画下去的时候不会偏移:
getHorizontalStyle () {
const d = document
const w = window.innerWidth || d.documentElement.clientWidth || d.body.clientWidth
const h = window.innerHeight || d.documentElement.clientHeight || d.body.clientHeight
let length = (h - w) / 2
let width = w
let height = h
switch (this.degree) {
case -90:
length = -length
/* eslint no-fallthrough: "error" */
case 90:
width = h
height = w
break
default:
length = 0
}
if (this.canvasBox) {
this.canvasBox.removeChild(document.querySelector('canvas'))
this.canvasBox.appendChild(document.createElement('canvas'))
/* eslint vue/no-async-in-computed-properties: "off" */
setTimeout(() => {
console.log(`测试执行`)
this.initCanvas()
}, 200)
}
return {
transform: `rotate(${this.degree}deg) translate(${length}px,${length}px)`,
width: `${width}px`,
height: `${height}px`,
transformOrigin: 'center center'
}
}
我现在就建一个项目,你帮我看下。多谢!
from canvas-draw.
@Louiszhai ,我新建了一个项目: https://github.com/ForkProjects/error-signature , 操作步骤如下:
$ cnpm i
$ npm run start
$ 打开页面:http://127.0.0.1:3000
$ 点击“进入签名页面”按钮
$ 在对应页面画布签名不起作用
$ 刷新当前页面重新签名成功
重现几率100%
耽误你时间了,非常感谢!
from canvas-draw.
终于等到了...
from canvas-draw.
你引用的组件内有包含canvas名称的组件,因此每次运行vue会抛出如下错误:
猜测可能是这个原因,导致原生canvas解析变慢了,延迟100毫秒左右去执行initCanvas,便能成功初始化画布。
另外,建议把无关组件全部注释掉,然后试着debug,祝顺利~
from canvas-draw.
@Louiszhai , 谢谢你的回复,你晚上工作的很晚啊!
这个签名的页面的代码是跟你的基本一样的代码。。。我又尝试了你提到的方法:
- 将对应的签名页面的
getHorizontalStyle
属性中的this.initCanvas()
设置了时间我100毫秒,重新运行,还是一样直接进去画布不能签名 - 将所有的无关代码注释掉,尝试重新运行,还是不行。
麻烦你能再帮我看下这个问题吗?新的修改的仓库代码在这里: https://github.com/ForkProjects/error-signature , 已经是最新的重现这个问题的代码。
Edit: debug发现getHorizontalStyle
属性中的下面代码实际是没有执行:
if (this.canvasBox) {
this.canvasBox.removeChild(document.querySelector('canvas'))
this.canvasBox.appendChild(document.createElement('canvas'))
/* eslint vue/no-async-in-computed-properties: "off" */
setTimeout(() => {
console.log(`测试执行`)
this.initCanvas()
}, 100)
}
from canvas-draw.
from canvas-draw.
好的,非常感谢,我看看 , @Louiszhai
from canvas-draw.
还是不行! 找不到原因了!
from canvas-draw.
我也遇到了,暂时是在initCanvas时先将画布清空。执行一次this.draw.clear(); 就没有这种情况了
from canvas-draw.
同样遇到了,模拟出了一种复现的情况:
<canvas ref="signature" style="width: 100%; height: 100%;" v-show="show"></canvas>
mounted() {
this.init()
setTimeout(() => {
this.show = true
}, 50)
}
methods: {
init() {
const canvas = this.$refs.signature;
this.draw = new Draw(canvas, 0);
}
}
添加了一个50毫秒的延迟,然后初始化还是正常初始化就可以模拟出这种情况,然后解决结果如下:
// 源码中这个width height 是 100% 而不是 px 单位的,所以我在原型上加了一个判断方法
let { width, height } = window.getComputedStyle(canvas, null);
Draw.prototype = {
isDraw() {
// 然后在调用这个的地方判断如果是false则重新初始化一遍就可以了
if (this.width === '100%' && this.height === '100%') return false
...
}
}
from canvas-draw.
同样遇到了,模拟出了一种复现的情况:
<canvas ref="signature" style="width: 100%; height: 100%;" v-show="show"></canvas> mounted() { this.init() setTimeout(() => { this.show = true }, 50) } methods: { init() { const canvas = this.$refs.signature; this.draw = new Draw(canvas, 0); } }
添加了一个50毫秒的延迟,然后初始化还是正常初始化就可以模拟出这种情况,然后解决结果如下:
// 源码中这个width height 是 100% 而不是 px 单位的,所以我在原型上加了一个判断方法 let { width, height } = window.getComputedStyle(canvas, null); Draw.prototype = { isDraw() { // 然后在调用这个的地方判断如果是false则重新初始化一遍就可以了 if (this.width === '100%' && this.height === '100%') return false ... } }
更新一下,H5上又发现了一种复现情况,当上一个页面有滚动条的时候,如果滚动条是滑在下面进入签名页面的时候,这时候就不能签名,猜测其实是签了,可能是因为滚动条的原因导致坐标异常了看不到签的位置,如果在进入签名页面的时候把scrollTop
置为0就可以正常签名了。
from canvas-draw.
辛苦,签名时,一定要剔除滚动条的干扰
from canvas-draw.
Related Issues (14)
- 希望提供文档与demo HOT 2
- Ubuntu下npm run dev 和build报错 HOT 3
- 当我页面上有其他dom结构时,绘画的起点就偏移了 ToT... HOT 4
- 请问怎么判断canvas的内容为空 HOT 1
- 我在vue中使用该插件,无法绘画 HOT 3
- 一旦页面发生了滚动,签名绘制出来的轨迹就出现偏差 HOT 2
- Cannot find module 'enhanced-resolve/lib/AliasPlugin' HOT 1
- 我想请教下,ios 签名,哪段代码是阻止橡皮筋效果的,我平时的签名canvas,到ios画画时候 界面都上下乱跑 HOT 1
- 这个插件怎么用。。 HOT 2
- 可以获取base64图片吗,生成的图片能透明吗 HOT 1
- IOS中不能签名啊? HOT 1
- 在iview的Modal组件中使用。默认代码造成canvas过大。 HOT 1
- 你好, 有计划更新在小程序里使用么? HOT 3
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from canvas-draw.