canvas电子画板 涂鸦、橡皮擦、保存图片,图片放大缩小

首先感谢作者https://ext.dcloud.net.cn/publisher?id=167918 (675***@qq.com) uniapp插件市场涂鸦示例作者,若作者发现此博客有不妥行为,可联系我进行删除。再此基础上增加涂鸦时对图片大放大缩小、橡皮擦功能功能,保存涂鸦部分使用html2canvas,原作者使用arraybuffer转base64,根据自己方式进行选择。

【主要功能】画笔,图纸的放大缩小,开始画笔进行绘制,关闭画笔进行双指放大、缩小移动,保存绘制截图

整个demo放在百度云盘。

链接:https://pan.baidu.com/s/1alpdzH6cJUTCq9aTOCxjxg 
提取码:1024 
--来自百度网盘超级会员V4的分享

 

页面代码index.vue
<template>
	<view class="wrap">
		<view class="wrap-head">
			<text @click="penOpen" v-if="close">开启画笔</text>
			<text @click="close=true" v-else class="wrap-head-close">关闭画笔</text>
			<text @click="eraser">橡皮擦</text>
			<text @click="subCanvas">保存涂鸦</text>
			<text @click="renderScript.emitData">截图</text>
		</view>
		<movable-area :style="'height:100vh;width: 750rpx;top:0px'">
			<movable-view :key="j" v-for="(i,j) in ImageInfo" class="handCenter"
				:style="{width:i.cavWidth+'px',height:i.cavHeight+'px'}" :disabled="!close" inertia="false"
				:scale="close" :scale-min="0.5" :scale-max="1.5" direction="all" @scale="onScale" :out-of-bounds='true'
				id="poster">
				
				<image class="cavImg" :style="{width:i.cavWidth+'px',height:i.cavHeight+'px'}" :src="i.url" />
			
				<canvas class="myCanvas" disable-scroll="true" :data-id="j" @touchstart="penStart" @touchmove="penMove"
					@touchend="penEnd" :canvas-id="'myCanvas'+j"></canvas>
				<view v-show="close" class="wrap-index" :style="{width:i.cavWidth+'px',height:i.cavHeight+'px'}"></view>
			</movable-view>
		</movable-area>
		<canvas v-show="false" class="picCut" id="picCut"></canvas>
		<view class="poster-view" v-if="posterImg.length>0">
			<image :src="posterImg" mode="scaleToFill"
				:style="'width:'+(posterWidth-30)+'px;height:'+(posterHeight-50)+'px;'"></image>
		</view>
		<view v-if="!close&&open" class="marking-tag3">
			<!-- 颜色 -->
			<view class="color">
				<block v-for="i in colorArr" :key="i.color">
					<view :class="{colorSelection:i.active}">
						<view @click="updateColor(i.color)" :style="{background: i.color}"></view>
					</view>
				</block>
			</view>
			<!-- 粗细 -->
			<view class="thickness">
				<view @tap="open=false" class="marking-tag4">收起</view>
				<block v-for="i in thickness" :key="i.thickness">
					<view :class="{colorSelection:i.active}">
						<view @click="updateThickness(i.thickness)" :style="{height: i.thickness/5+'rpx'}"></view>
					</view>
				</block>
			</view>
			<!-- 擦除 -->
			<view class="Erase">
				<view @click="retDraw" style="color:red">清除</view>
			</view>
		</view>
		<view v-else-if="!close&&!open" @tap="open=true" class="marking-tag2">展开</view>
	</view>
</template>

<script>
	import Mycanvas from "../../static/js/handwriting.js";
	import {
		base64ToPath,
		pathToBase64
	} from '@/utils/image-tools.js';
	export default {
		data() {
			return {
				close: true,
				open: true,
				colorArr: [
					//画笔颜色
					{
						color: "#ff0000",
						active: true
					},
					{
						color: "#1c9d02",
						active: false
					},
					{
						color: "#000000",
						active: false
					},
					{
						color: "#006ce6",
						active: false
					},
					{
						color: "#efaa03",
						active: false
					},
					{
						color: "rgba(255, 255, 0, 0.5)",
						active: false
					},
				],
				thickness: [
					//画笔粗细
					{
						thickness: 10,
						active: false
					},
					{
						thickness: 20,
						active: false
					},
					{
						thickness: 30,
						active: true
					},
					{
						thickness: 40,
						active: false
					},
					{
						thickness: 50,
						active: false
					}
				],
				//当前的图片路径
				answerPhoto: ["/static/img/img3.jpg"],
				// 保存图片、画布宽高
				ImageInfo: [],
				// new出来的实例
				canvasInfo: [],
				// 有涂鸦的图片的下标
				imgIndex: [],
				scale: 1,
				isDraw: true,
				activeColor: '',
				posterImg: '',
				posterHeight: 0,
				posterWidth: 0,
			};
		},
		onReady() {
			// 获取图片信息
			this.getImageInfo();
		},
		watch: {
			// new出canvas的实例
			ImageInfo(ImageInfo) {
				this.ImageInfo.map((i, j) => {
					this.canvasInfo.push(
						new Mycanvas({
							lineColor: this.lineColor,
							lineSise: this.lineSise,
							canvasName: "myCanvas" + j,
						})
					);
				});
			}
		},
		computed: {
			//颜色
			lineColor: {
				get() {
					let [color] = this.colorArr.filter(i => i.active);
					return color.color;
				}
			},
			//粗细
			lineSise: {
				get() {
					let [thicknes] = this.thickness.filter(i => i.active);
					return thicknes.thickness;
				}
			}
		},
		methods: {
			penOpen() {
				this.close = false;
				this.isDraw = true;
				this.updateColor('rgba(255, 0, 0, 1)')
			},
			eraser() {
				this.close = false;
				this.isDraw = false
				this.updateColor('rgba(255, 255, 255, 0)')
				// if (this.close) return;
				// let index = event.target.dataset.id;
				// //#ifdef H5
				// index = event.currentTarget.dataset.id;
				// //#endif

				// // #ifdef  MP-WEIXIN
				// index = event.target.dataset.id;
				// // #endif
				// // 涂鸦后的下标
				// this.imgIndex.push(index);

				// this.canvasInfo[0].eraser();

				// context.clearRect(0,0,50,50);
			},
			onMove: function(e) {
				// if(this.isMoveTrue){
				// 	this.showTis = false
				// 	this.moveX = e.detail.x
				// }

			},
			onScale(e) {
				console.log(e)
				this.scale = e.detail.scale
			},
			// 获取图片信息
			async getImageInfo() {
				let newArr = [];
				for (let i = 0; i < this.answerPhoto.length; i++) {
					let [, image] = await uni.getImageInfo({
						src: this.answerPhoto[i]
					});
					newArr.push({
						url: this.answerPhoto[i],
						cavWidth: this.widths(image.width),
						cavHeight: this.heights(image.height, image.width),
						imgWidth: image.width,
						imgHeight: image.height
					});
				}
				this.ImageInfo = newArr;
			},
			//计算宽
			widths(imgWidth) {
				const res = uni.getSystemInfoSync();
				//屏幕宽
				if (res.windowWidth * 0.95 <= imgWidth) {
					return res.windowWidth * 0.95; //rpx
				} else {
					return imgWidth;
				}
			},
			//计算高
			heights(imgHeight, imgWidth) {
				const res = uni.getSystemInfoSync();
				if (res.windowWidth * 0.95 <= imgWidth) {
					let b = (res.windowWidth * 0.95) / imgWidth;
					return b * imgHeight;
				} else {
					return imgHeight;
				}
			},
			// 笔迹粗细滑块
			updateThickness(value) {
				this.canvasInfo.map(i => {
					i.selectSlideValue(value);
				});
				this.thickness.map(i => {
					if (i.thickness == value) {
						i.active = true;
					} else {
						i.active = false;
					}
					return i;
				});
			},
			// 选择画笔颜色
			updateColor(color) {
				this.canvasInfo.map(i => {
					i.selectColorEvent(color);
				});
				this.colorArr.map(i => {
					if (i.color == color) {
						i.active = true;
					} else {
						i.active = false;
					}
					this.activeColor = i.color
					return i;
				});
			},
			// 清除画布
			retDraw() {
				this.canvasInfo.map(i => {
					i.retDraw();
				});
			},
			// 笔迹开始
			penStart(event) {
				if (this.close) return;
				let index = event.target.dataset.id;
				//#ifdef H5
				index = event.currentTarget.dataset.id;
				//#endif

				// #ifdef  MP-WEIXIN
				index = event.target.dataset.id;
				// #endif
				// 涂鸦后的下标
				this.imgIndex.push(index);

				this.canvasInfo[index].penStart(event, this.scale, this.isDraw);
			},
			// 笔迹移动
			penMove(event) {
				if (this.close) return;
				let index = event.target.dataset.id;
				// #ifdef  MP-WEIXIN
				index = event.target.dataset.id;
				// #endif
				//#ifdef H5
				index = event.currentTarget.dataset.id;
				//#endif
				this.canvasInfo[index].penMove(event, this.scale, this.isDraw);
			},
			// 笔迹结束
			penEnd(event) {
				if (this.close) return;
				let index = event.target.dataset.id;
				// #ifdef  MP-WEIXIN
				index = event.target.dataset.id;
				// #endif
				//#ifdef H5
				index = event.currentTarget.dataset.id;
				//#endif
				this.canvasInfo[index].penEnd(event, this.scale, this.isDraw);
			},
			// 保存涂鸦的图片
			async subCanvas() {
				// let index = [...new Set(this.imgIndex)];
				// if (index.length !== 0) {
				// 	for (let i = 0; i < index.length; i++) {
				// 		// h5环境下为base
				// 		const data = await this.canvasInfo[index[i]].saveCanvas();
				// 		console.log(data);

				// 	}
				// }
				let that = this
				let dom = null
				const query = uni.createSelectorQuery().in(this);

				query.select('.handCenter').boundingClientRect(data => {
					console.log(data)
					dom = data


				}).exec();
				query.select('.picCut').boundingClientRect(canvas => {
					console.log(dom)
					canvas.width = dom.width;
					canvas.height = dom.height;
					let ctx = uni.createCanvasContext("picCut")
					console.log(ctx)
					let img = ctx.drawImage(dom, 0, 0, dom.width, dom.width)
					console.log(img)
					let previewImg = canvas.toDataURL('image/png')
					console.log(previewImg)

				}).exec()




				// query.select('.picCut').boundingClientRect(data => {
				//     console.log(data)
				// 	canvas = data
				// }).exec();
				// let video = uni.createSelectorQuery().selectAll(".handCenter")
				// console.log(video)
				// let canvas = document.createElement('canvas')
				// console.log("######",dom)
				// console.log(canvas)

				// // console.log(canvas)
				// const ctx = canvas.getContext('2d')
				// // console.log(ctx)
				// ctx.drawImage(video, 0, 0, w, h)
				// let previewImg = canvas.toDataURL('image/png')
				// console.log(canvas.toDataURL('image/png'))
				// this.cutPicUploadForm.imgStr = canvas.toDataURL('image/png')

			},
			async receiveRenderData(imgData) {
				this.posterHeight = imgData.height;
				this.posterWidth = imgData.width;
				let imgPath = await base64ToPath(imgData.imgVal, '.jpeg');
				console.log("海报宽度", this.posterWidth);
				console.log("海报高度", this.posterHeight);
				this.posterImg = imgPath;
				// uni.hideLoading();
				// uni.showToast({
				// 	title:"success !",
				// 	icon:'success'
				// })
			}
		}
	};
</script>

<script module="renderScript" lang="renderjs">
	import html2canvas from 'html2canvas';
	export default {
		data() {
			return {

			}
		},
		//页面加载就生成图片
		/* 	mounted() {
				let that=this;
				this.$ownerInstance.callMethod('showLoading',true);
				setTimeout(function(){
					that.emitData();
				},5000);
			}, */
		methods: {
			// 发送数据到逻辑层
			emitData(e, ownerVm) {
				// this.$ownerInstance.callMethod('showLoading',true);
				const dom = document.getElementById('poster');
				let width = dom.offsetWidth;
				let height = dom.offsetHeight;
				let windowHeight = uni.getSystemInfoSync().windowHeight;
				let windowWidth = uni.getSystemInfoSync().windowWidth;
				html2canvas(dom, {
					width: width,
					height: height,
					//scale:2.5, // 缩放倍数
					scrollY: 0, // html2canvas默认绘制视图内的页面,需要把scrollY,scrollX设置为0
					scrollX: 0,
					x: 0,
					y: 0,
					windowWidth: windowWidth,
					windowHeight: windowHeight,
					useCORS: true, //支持跨域,但好像没什么用
				}).then((canvas) => {
					let imgVal = canvas.toDataURL('image/png');
					console.log(imgVal)
					this.$ownerInstance.callMethod('receiveRenderData', {
						imgVal: imgVal,
						height: height,
						width: width
					});
				});
			}
		}
	};
</script>

<style lang="scss" scoped>
	.colorSelection {
		background: #ccc;
	}

	.wrap {
		width: 100%;
		height: 100%;
		margin: 30upx 0;
		overflow: hidden;
		display: flex;
		align-content: center;
		flex-direction: column;
		justify-content: center;
		font-size: 28upx;

		.marking-tag2 {
			position: fixed;
			right: 0;
			z-index: 10;
			bottom: 280rpx;
			background-color: #fff;
			padding: 5rpx 10rpx;
		}

		.marking-tag3 {
			position: fixed;
			right: 0;
			bottom: 210rpx;
			z-index: 10;

			.Erase {
				display: flex;
				justify-content: space-between;
				background: #fff;

				>view {
					padding: 15rpx 0;
					text-align: center;
					flex-grow: 1;
				}
			}

			.color {
				display: flex;

				.colorSelection {
					background: #ccc;
				}

				>view {
					background: #fff;
					padding: 15rpx 30rpx;

					>view {
						width: 25rpx;
						height: 25rpx;
						border-radius: 50%;
					}
				}
			}

			.thickness {
				display: flex;
				justify-content: space-between;
				background: #fff;
				position: relative;

				>view {
					flex-grow: 1;
					display: flex;
					align-items: center;
					justify-content: center;
					padding: 20rpx 0;

					>view {
						width: 40rpx;
						background: #666;
					}
				}

				.marking-tag4 {
					position: absolute;
					background: #fff;
					left: 0;
					padding: 5rpx 15rpx;
					transform: translateX(-100%);
				}
			}
		}

		.wrap-head {
			display: flex;
			padding: 0 5rpx;
			justify-content: center;

			.wrap-head-close {
				background: #8799a3;
			}

			text {
				background-color: #39b54a;
				color: white;
				border-radius: 5upx;
				font-size: 30rpx;
				padding: 10rpx 15rpx;
				margin: 15rpx;
			}
		}

		.myCanvas {
			// background: transparent;
			width: 100%;
			height: 100%;
		}

		.handCenter {
			border: 4upx dashed #e9e9e9;
			flex: 5;
			overflow: hidden;
			background: transparent;
			box-sizing: border-box;
			max-width: 95%;
			margin: 5rpx auto;
			position: relative;
			z-index: 2;
		}

		.cavImg {
			position: absolute;
			z-index: -1;
		}

		.wrap-index {
			position: absolute;
			z-index: 10;
			top: 0;
			background-color: transparent;
		}
	}

	.poster-view {
		display: flex;
		flex-direction: column;
		justify-content: center;
		align-items: center;
		margin: 20px 0px;
	}
</style>

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值