123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325 |
- <template>
- <view class="canvas">
- <canvas canvas-id="myCanvas" :style="{width: width+'px',height: height+'px'}"></canvas>
- </view>
- </template>
- <!--
- list参数说明:
- use:用途
- imageType:用于区分图片是本地图片还是网络图片本地图片需要取
- 图片渲染:
- type: 'image',
- x: X轴位置,
- y: Y轴位置,
- path: 图片路径,
- width: 图片宽度,
- height: 图片高度,
- rotate: 旋转角度
- shape: 形状,默认无,可选值:circle 圆形
- area: {x,y,width,height} // 绘制范围,超出该范围会被剪裁掉 该属性与shape暂时无法同时使用,area存在时,shape失效
-
- 文字渲染:
- type: 'text',
- x: X轴位置,
- y: Y轴位置,
- text: 文本内容,
- size: 字体大小,
- textBaseline: 基线 默认top 可选值:'top'、'bottom'、'middle'、'normal'
- color: 颜色
-
- 多行文字渲染:
- type: 'textarea',
- x: X轴位置,
- y: Y轴位置,
- width:换行的宽度
- height: 高度,溢出会展示“...”
- lineSpace: 行间距
- text: 文本内容,
- size: 字体大小,
- textBaseline: 基线 默认top 可选值:'top'、'bottom'、'middle'、'normal'
- color: 颜色
- -->
- <script>
- export default {
- name: "Poster",
- props: {
- // 绘制队列
- data: {
- type: Object,
- required: true
- },
- width: {
- type: Number,
- required: true
- },
- height: {
- type: Number,
- required: true
- },
- clicknum:{
- type: Number,
- default: 0
- },
- backgroundColor: {
- type: String,
- default: 'rgba(0,0,0,0)'
- }
- },
- emit: ['on-success', 'on-error'],
- data() {
- return {
- posterUrl: '',
- ctx: null, //画布上下文
- counter: -1, //计数器
- drawPathQueue: [], //画图路径队列
- };
- },
- watch: {
- data:{
- handler(newVal, oldVal){
- console.log(newVal,11111)
- this.list = newVal.list
- if(this.list.length!=0){
- this.generateImg()
- console.log('mounted')
- }
- },
- },
- drawPathQueue(newVal, oldVal) {
- console.log(this.ctx,111111)
- // 绘制单行文字
- const fillText = (textOptions) => {
- console.log(textOptions)
- this.ctx.setFillStyle(textOptions.color)
- this.ctx.setFontSize(textOptions.size)
- this.ctx.setTextBaseline(textOptions.textBaseline || 'top')
- this.ctx.fillText(textOptions.text, textOptions.x, textOptions.y)
- }
- // 绘制段落
- const fillParagraph = (textOptions) => {
- this.ctx.setFontSize(textOptions.size)
- let tempOptions = JSON.parse(JSON.stringify(textOptions));
- // 如果没有指定行间距则设置默认值
- tempOptions.lineSpace = tempOptions.lineSpace ? tempOptions.lineSpace : 10;
- // 获取字符串
- let str = textOptions.text;
- console.log()
- // 计算指定高度可以输出的最大行数
- // let lineCount = Math.floor((tempOptions.height + tempOptions.lineSpace) / (tempOptions.size +
- // tempOptions.lineSpace))
- // 初始化单行宽度
- let lineWidth = 0;
- let lastSubStrIndex = 0; //每次开始截取的字符串的索引
- // 构建一个打印数组
- let strArr = str.split("");
- let drawArr = [];
- let text = "";
- while (strArr.length) {
- let word = strArr.shift()
- text += word;
- let textWidth = this.ctx.measureText(text).width;
- if (textWidth > textOptions.width) {
- // 因为超出宽度 所以要截取掉最后一个字符
- text = text.substr(0, text.length - 1)
- drawArr.push(text)
- text = "";
- // 最后一个字还给strArr
- strArr.unshift(word)
- } else if (!strArr.length) {
- drawArr.push(text)
- }
- }
- if (drawArr.length > tempOptions.lineSpace) {
- // 超出最大行数
- drawArr.length = tempOptions.lineSpace;
- let pointWidth = this.ctx.measureText('...').width;
- let wordWidth = 0;
- let wordArr = drawArr[drawArr.length - 1].split("");
- let words = '';
- while (pointWidth > wordWidth) {
- words += wordArr.pop();
- wordWidth = this.ctx.measureText(words).width
- }
- drawArr[drawArr.length - 1] = wordArr.join('') + '...';
- }
- // 打印
- for (let i = 0; i < drawArr.length; i++) {
- tempOptions.y = tempOptions.y + tempOptions.size * i + tempOptions.lineSpace * i; // y的位置
- tempOptions.text = drawArr[i]; // 绘制的文本
- fillText(tempOptions)
- }
- }
- // 绘制背景
- this.ctx.setFillStyle(this.backgroundColor);
- this.ctx.fillRect(0, 0, this.width, this.height);
- /* 所有元素入队则开始绘制 */
- if (newVal.length === this.list.length) {
- try {
- console.log(this.list,1111111)
- // console.log('生成的队列:' + JSON.stringify(newVal));
- console.log('开始绘制...')
- for (let i = 0; i < this.drawPathQueue.length; i++) {
- for (let j = 0; j < this.drawPathQueue.length; j++) {
- let current = this.drawPathQueue[j]
- /* 按顺序绘制 */
- if (current.index === i) {
- /* 文本绘制 */
- if (current.type === 'text') {
- fillText(current)
- this.counter--
- }
- /* 多行文本 */
- if (current.type === 'textarea') {
- fillParagraph(current)
- this.counter--
- }
- /* 图片绘制 */
- if (current.type === 'image') {
- if (current.area) {
- // 绘制绘图区域
- this.ctx.save()
- this.ctx.beginPath(); //开始绘制
- this.ctx.rect(current.area.x, current.area.y, current.area.width, current.area
- .height)
- this.ctx.clip();
- // 设置旋转中心
- let offsetX = current.x + Number(current.width) / 2;
- let offsetY = current.y + Number(current.height) / 2;
- this.ctx.translate(offsetX, offsetY)
- let degrees = current.rotate ? Number(current.rotate) % 360 : 0;
- this.ctx.rotate(degrees * Math.PI / 180)
- this.ctx.drawImage(current.path, current.x - offsetX, current.y - offsetY,
- current.width, current.height)
- this.ctx.closePath();
- this.ctx.restore(); // 恢复之前保存的上下文
- } else if (current.shape == 'circle') {
- this.ctx.save(); // 保存上下文,绘制后恢复
- this.ctx.beginPath(); //开始绘制
- //先画个圆 前两个参数确定了圆心 (x,y) 坐标 第三个参数是圆的半径 四参数是绘图方向 默认是false,即顺时针
- let width = (current.width / 2 + current.x);
- let height = (current.height / 2 + current.y);
- let r = current.width / 2;
- this.ctx.arc(width, height, r, 0, Math.PI * 2);
- //画好了圆 剪切 原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内 这也是我们要save上下文的原因
- this.ctx.clip();
- // 设置旋转中心
- let offsetX = current.x + Number(current.width) / 2;
- let offsetY = current.y + Number(current.height) / 2;
- this.ctx.translate(offsetX, offsetY)
- let degrees = current.rotate ? Number(current.rotate) % 360 : 0;
- this.ctx.rotate(degrees * Math.PI / 180)
- this.ctx.drawImage(current.path, current.x - offsetX, current.y - offsetY,
- current.width, current.height)
- this.ctx.closePath();
- this.ctx.restore(); // 恢复之前保存的上下文
- } else {
- this.ctx.drawImage(current.path, current.x, current.y, current.width, current
- .height)
- }
- this.counter--
- }
- }
- }
- }
- } catch (err) {
- console.log(err)
- this.$emit('on-error', err)
- }
- }
- },
- counter(newVal, oldVal) {
- if (newVal === 0) {
- this.ctx.draw()
- /* draw完不能立刻转存,需要等待一段时间 */
- setTimeout(() => {
- uni.canvasToTempFilePath({
- canvasId: 'myCanvas',
- success: (res) => {
- console.log('in canvasToTempFilePath');
- // 在H5平台下,tempFilePath 为 base64
- // console.log('图片已保存至本地:', res.tempFilePath)
- this.posterUrl = res.tempFilePath;
- this.$emit('on-success', res.tempFilePath)
- },
- fail: (res) => {
- console.log(res)
- }
- }, this)
- }, 1000)
- }
- }
- },
- mounted() {
- this.ctx = uni.createCanvasContext('myCanvas', this)
-
- },
- methods: {
- create() {
- this.generateImg()
- },
- /**
- * 图片圆角设置
- * @param string x x轴位置
- * @param string y y轴位置
- * @param string w 图片宽
- * @param string y 图片高
- * @param string r 圆角值
- */
- generateImg() {
- console.log('generateimg',this.drawPathQueue)
- this.counter = this.list.length
- this.drawPathQueue = []
- /* 将图片路径取出放入绘图队列 */
- for (let i = 0; i < this.list.length; i++) {
- let current = this.list[i]
- console.log(this.list[i])
- current.index = i
- /* 如果是文本直接放入队列 */
- if (current.type === 'text' || current.type === 'textarea' || current.type === 'image' && current.use !== 'bg'&¤t.use !== 'head' ) {
- this.drawPathQueue.push(current)
- continue
- }
- if( current.use === 'head'&¤t.imageType === 'bd'){
- this.drawPathQueue.push(current)
- continue
- }
- /* 图片需获取本地缓存path放入队列 */
- uni.getImageInfo({
- src: current.path,
- success: (res) => {
- current.path = res.path
- this.drawPathQueue.push(current)
- }
- })
- }
- },
- saveImg() {
- uni.canvasToTempFilePath({
- canvasId: 'myCanvas',
- success: (res) => {
- // 在H5平台下,tempFilePath 为 base64
- uni.saveImageToPhotosAlbum({
- filePath: res.tempFilePath,
- success: () => {
- console.log('save success');
- }
- });
- }
- })
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .canvas {
- position: fixed;
- top: 100rpx;
- left: 750rpx;
- }
- </style>
|