# canvas生成笔记图片
import styles from './index.less'
import { PureComponent, createRef } from 'react'
import QRCode from 'qrcode'
import html2canvas from 'html2canvas'
import classnames from 'classnames'
import preventScroll from 'prevent-scroll'
import growingService from '@xb/services/growing'
import MaskBasic from '@xb/components/Mask/Basic'
import MaskCenter from '@xb/components/Mask/Center'
import ButtonBasic from '@xb/components/Button/Basic'
import Line from '@xb/components/Line/Basic'
import { MountPoint, sleep } from '@xb/utils/dom'
import { device } from '@xb/utils/userAgent'
import creditService from '@xb/services/credit'
import iconLogoComplete from '@xb/assets/icons/icon-logo-complete.png'
import ellipseBg from '@xb/assets/ellipse-bg.png'
import userImg from '@xb/assets/user.png'
class ModalComponent extends PureComponent {
constructor(props) {
super(props)
const { onClick, uniqueId, noteId } = this.props
this.onClick = onClick
this.node = createRef()
this.container = createRef()
this.saveImage = createRef()
this.avatar = createRef()
this.startTime = ''
this.endTime = ''
const link = `${window.location.origin}/course/note/landing/${uniqueId}/${noteId}?sourceType=h5wxshare`
this.action.generateQR(link)
this.state = {
once: true,
qrcode: false,
}
}
action = {
generateQR: async url => {
try {
const baseImage = await QRCode.toDataURL(url)
const image = new Image()
image.src = baseImage
this.node.current.appendChild(image)
this.setState({ qrcode: true })
} catch (err) {
console.error(err)
}
},
generateImage: async () => {
const canvas = this.action.drawCanvas(this.container.current)
await sleep(300)
html2canvas(this.container.current, {
useCORS: true,
logging: false,
canvas,
scale: 10,
letterRendering: true,
}).then(canvas => {
const image = new Image()
image.src = canvas.toDataURL('image/png')
this.saveImage.current.appendChild(image)
})
},
checkPicture: async url => {
return new Promise(resolve => {
const image = new Image()
image.src = url
image.onload = () => {
resolve(url)
}
image.onerror = () => {
resolve(userImg)
}
})
},
DPR: () => {
if (window.devicePixelRatio && window.devicePixelRatio > 1) {
return window.devicePixelRatio
}
return 1
},
parseValue: value => {
return parseInt(value, 10)
},
drawCanvas: node => {
const box = window.getComputedStyle(node)
// DOM 节点计算后宽高
const width = this.action.parseValue(box.width)
const height = this.action.parseValue(box.height)
// 获取像素比
const scaleBy = this.action.DPR()
// 创建自定义 canvas 元素
const canvas = document.createElement('canvas')
// 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比
canvas.width = width * scaleBy
canvas.height = height * scaleBy
// 设定 canvas css宽高为 DOM 节点宽高
canvas.style.width = `${width}px`
canvas.style.height = `${height}px`
// 获取画笔
const context = canvas.getContext('2d')
// 【重要】关闭抗锯齿
context.mozImageSmoothingEnabled = false
context.msImageSmoothingEnabled = false
context.imageSmoothingEnabled = false
// 将所有绘制内容放大像素比倍
context.scale(scaleBy, scaleBy)
return canvas
},
touchstart: () => {
this.startTime = +new Date()
},
touchend: () => {
this.endTime = +new Date()
const { uniqueId } = this.props
if (this.endTime - this.startTime > 700) {
const { payload } = this.props
growingService.noteShareImage(payload)
try {
creditService.shareNote(uniqueId)
} catch (error) {
console.error(error)
}
}
},
}
render() {
const { userUrl, userName, createdTime, content, productTitle } = this.props
const { once, qrcode } = this.state
const isIOSBox = device.isIOS ? styles.isIOSBox : ''
const isIOSNote = device.isIOS ? styles.isIOSNote : ''
const avatar = this.action.checkPicture(userUrl)
avatar.then(url => {
this.avatar.current.src = url
if (once && qrcode) {
this.action.generateImage()
this.setState({ once: false })
}
})
return (
<MaskBasic onClick={this.onClick}>
<MaskCenter>
<div className={classnames(styles.box, isIOSBox)} onClick={e => e.stopPropagation()}>
<div
ref={this.saveImage}
className={styles.containerImage}
onTouchStart={this.action.touchstart}
onTouchEnd={this.action.touchend}
/>
<div ref={this.container} className={styles.container}>
<img className={styles.logo} src={iconLogoComplete} />
<img className={styles.ellipse} src={ellipseBg} />
<div className={styles.content}>
<div className={styles.product}>
<span>来自课程</span>
<p>{productTitle}</p>
</div>
<div className={styles.userInfo}>
<img ref={this.avatar} className={styles.avatar} crossOrigin='anonymous' />
<div className={styles.user}>
<h3>{userName}</h3>
<p>{createdTime}</p>
</div>
</div>
<p className={classnames(styles.note, isIOSNote)}>{content}</p>
<Line className={styles.line} size={15} />
<div className={styles.qrcodeInfo}>
<div className={styles.qrcode} ref={this.node} />
<div className={styles.description}>
<h3>长按识别二维码</h3>
<p>查看完整笔记,亦可点赞评论</p>
</div>
</div>
</div>
</div>
<div className={styles.button}>
<ButtonBasic style={{ width: '100%', color: '#222222', fontWeight: 400, boxShadow: 'none' }}>长按按钮分享笔记</ButtonBasic>
</div>
</div>
<div className={styles.close} />
</MaskCenter>
</MaskBasic>
)
}
}
class GlobalModalSharePicture extends MountPoint {
hide = () => {
preventScroll.off()
this.removeMountPoint()
}
show = options => {
preventScroll.on()
this.render(<ModalComponent onClick={this.hide} {...options} />)
}
}
export default new GlobalModalSharePicture()
← React.lazy 图片上传 →