经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » HTML/CSS » HTML5 » 查看文章
基于Html5 canvas实现裁剪图片和马赛克功能及又拍云上传图片 功能
来源:jb51  时间:2019/7/10 9:22:22  对本文有异议

1.核心功能

  此组件功能包含:

    图片裁剪(裁剪框拖动,裁剪框改变大小);

    图片马赛克(绘制马赛克,清除马赛克);

    图片预览、图片还原(返回原图、返回处理图);

    图片上传(获取签名、上传图片)。

2.核心逻辑

  2.1图片裁剪

  获取裁剪框(矩形)相对于画布的位置(左上)和裁剪框的height、width。获取(getImageData)canvas相应位置的图片对象(ImageData)。清空canvas画布。在canvas画布的相应位置绘制(putImageData)获取的图片对象(ImageData)。生成预览图。

  2.2图片马赛克

  马赛克的绘制,就是在以鼠标划过路径(画笔宽度)为中心的区域,重新绘制成其他的颜色。一般结果是,会取周围的相近的颜色。

  取色方法:

  1)比如现有一鼠标划过的点的坐标(x,y),定义一个矩形左上角坐标取(x,y),宽30px,高30px。我们把矩形宽高都除以5(分成5份,可以自定义为n份),所以现在是25个6px的小格子。每个小格子宽高都是6px。

  2)然后,我们随机获取一个小格子,获取(getImageData)这个小格子的图片对象(ImageData);再随机获取此图片对象上某个像素点(宽1px,高1px)的颜色color(rgba:ImageData.data[0],ImageData.data[1],ImageData.data[2],ImageData.data[3]);最后我们把第一个6x6px的小格子的每个像素点的颜色都设置为color。

  3)其他24个小格子的颜色,遍历2步骤即可。

  2.3清除马赛克

  我们需要理解一个问题,不管是绘制马赛克,还是清除马赛克,其本质都是在绘制图片。我们在某个位置绘制了马赛克,清除的时候,就是把原图在当前位置的图片对象再画出来。就达到了清除的效果。所以,我们需要备份一个canvas,和原图一模一样,清除的时候,需要获取备份画布上对应位置的图像,绘制到马赛克的位置。

  2.4图片预览

  图片预览就是获取裁剪框的区域,获取区域内的图片对象。再绘制到画布上。

  2.5图片还原至原图

  清空画布,再次绘制原图

  2.6还原至已操作图片

  预览是保存画布图片对象(ImageData),清空画布,绘制保存的图片对象至画布

  2.7图片上传

  获取(toDataURL)canvas图片路径,将获取到的base64图片转化为File对象。进行上传。

3.完整代码如下:

  1. <template>
  2. <div class="canvas-clip" :loading="loading">
  3. <div
  4. v-show="isDrop"
  5. class="canvas-mainBox"
  6. ref="canvas-mainBox"
  7. id="canvas-mainBox"
  8. @mousedown.stop="startMove($event)"
  9. >
  10. <div class="canvas-minBox left-up" @mousedown.stop="startResize($event,0)"></div>
  11. <div class="canvas-minBox up" @mousedown.stop="startResize($event,1)"></div>
  12. <div class="canvas-minBox right-up" @mousedown.stop="startResize($event,2)"></div>
  13. <div class="canvas-minBox right" @mousedown.stop="startResize($event,3)"></div>
  14. <div class="canvas-minBox right-down" @mousedown.stop="startResize($event,4)"></div>
  15. <div class="canvas-minBox down" @mousedown.stop="startResize($event,5)"></div>
  16. <div class="canvas-minBox left-down" @mousedown.stop="startResize($event,6)"></div>
  17. <div class="canvas-minBox left" @mousedown.stop="startResize($event,7)"></div>
  18. </div>
  19. <!-- 画布 -->
  20. <canvas
  21. class="canvas-area"
  22. ref="canvas"
  23. id="canvas"
  24. :width="canvasWidth"
  25. :height="canvasHeight"
  26. @mousedown.stop="startMove($event)"
  27. :class="{hoverPaint:isMa,hoverClear:isMaClear}"
  28. ></canvas>
  29. <!-- 备份画布 -->
  30. <canvas class="canvas-copy" ref="canvasCopy" :width="canvasWidth" :height="canvasHeight"></canvas>
  31. <div class="canvas-btns">
  32. <button v-if="backBtn" @click="clipBack">返回</button>
  33. <button :class="{active:btnIndex==0}" @click="sourceImg">原图</button>
  34. <button :class="{active:btnIndex==1}" @click="paintRectReady" :disabled="isDisabled">马赛克</button>
  35. <button :class="{active:btnIndex==2}" @click="paintRectClearReady" :disabled="isDisabled">橡皮擦</button>
  36. <button :class="{active:btnIndex==3}" @click="clipReady" :disabled="isDisabled">裁剪</button>
  37. <button :class="{active:btnIndex==4}" @click="clipPosition">预览</button>
  38. <button @click="getSignature">上传</button>
  39. <button class="close" @click="canvasClose()">x</button>
  40. <!-- <div class="paint-size" v-if="isMaClear || isMa">
  41. <span>画笔大小</span>
  42. <input :defaultValue="maSize" v-model="maSize" max="100" min="1" type="range">
  43. <span class="size-num">{{maSize}}</span>
  44. </div> -->
  45. </div>
  46. </div>
  47. </template>
  48. <script>
  49. import axios from "axios";
  50. import md5 from "js-md5";
  51. import req from "../../axios/config";
  52. export default {
  53. props: ["imgUrl"],
  54. data() {
  55. return {
  56. resizeFX: "",
  57. movePrev: "",
  58. canvasWidth: 800, // 画布宽
  59. canvasHeight: 600, // 画布高
  60. loading: false,
  61. isDrop: false, // 裁剪
  62. isMa: false, // 马赛克
  63. maSize: 30, // 马赛克大小
  64. isMaClear: false, // 清除马赛克
  65. backBtn: false, // 返回按钮
  66. isDisabled: false,//禁用按钮
  67. btnIndex: 0,//当前按钮
  68. mouseX:'',// 鼠标位置
  69. mouseY:'',
  70. clipEle: "", // 裁剪框元素
  71. canvasDataSession: "", // 预览前的画布信息
  72. canvas: "", // 画布
  73. ctx: "", // 画布上下文
  74. canvasCopy: "", // copy画布
  75. ctxCopy: "", // copy画布上下文
  76. uploadOption: { // 图片上传参数
  77. path: "",
  78. policy: "",
  79. signature: "",
  80. username: ""
  81. }
  82. };
  83. },
  84. mounted() {
  85. this.clipEle = this.$refs["canvas-mainBox"];
  86. this.canvas = this.$refs["canvas"];
  87. this.ctx = this.canvas.getContext("2d");
  88. this.canvasCopy = this.$refs["canvasCopy"];
  89. this.ctxCopy = this.canvasCopy.getContext("2d");
  90. this.draw();
  91. },
  92. methods: {
  93. // 创建图片
  94. draw() {
  95. var img = new Image();
  96. img.setAttribute('crossOrigin', 'anonymous');
  97. img.onload = () => {
  98. this.ctx.drawImage(img, 0, 0, 800, 600);
  99. this.ctxCopy.drawImage(img, 0, 0, 800, 600);
  100. };
  101. img.src = this.imgUrl + '?time=' + new Date().valueOf();
  102. },
  103. //预览 计算裁剪框的位置(左上坐标)
  104. clipPosition() {
  105. this.isDisabled = true;
  106. this.backBtn = true;
  107. this.isMa = false;
  108. this.isMaClear = false;
  109. this.btnIndex = 4;
  110. //画布位置
  111. var canvasPx = this.canvas.offsetLeft,
  112. canvasPy = this.canvas.offsetTop;
  113. if (this.isDrop) {
  114. // 裁剪框位置
  115. var clipPx = this.clipEle.offsetLeft,
  116. clipPy = this.clipEle.offsetTop,
  117. x = clipPx - canvasPx,
  118. y = clipPy - canvasPy,
  119. w = this.clipEle.offsetWidth,
  120. h = this.clipEle.offsetHeight,
  121. // 预览图居中
  122. positionX = 400 - this.clipEle.offsetWidth / 2,
  123. positionY = 300 - this.clipEle.offsetHeight / 2;
  124. } else {
  125. // 没有裁剪框,保存完整图片
  126. var x = 0,
  127. y = 0,
  128. w = this.canvas.offsetWidth,
  129. h = this.canvas.offsetHeight,
  130. // 预览图居中
  131. positionX = 0,
  132. positionY = 0;
  133. }
  134. var imageData = this.ctx.getImageData(x, y, w, h);
  135. this.canvasDataSession = this.ctx.getImageData(
  136. 0,
  137. 0,
  138. this.canvasWidth,
  139. this.canvasHeight
  140. );
  141. this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  142. this.ctx.putImageData(imageData, positionX, positionY);
  143. this.clipEle.style.display = "none";
  144. this.canvasCopy.style.display = "none";
  145. },
  146. // 返回预览前状态
  147. clipBack() {
  148. this.btnIndex = -1;
  149. this.backBtn = false;
  150. this.isDisabled = false;
  151. this.isDrop = false;
  152. this.ctx.putImageData(this.canvasDataSession, 0, 0);
  153. this.canvasCopy.style.display = "block";
  154. },
  155. // 原图
  156. sourceImg() {
  157. this.isDisabled = false;
  158. this.btnIndex = 0;
  159. this.backBtn = false;
  160. this.isMa = false;
  161. this.isDrop = false;
  162. this.isMaClear = false;
  163. var img = new Image();
  164. this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
  165. img.setAttribute('crossOrigin', 'anonymous');
  166. img.onload = () => {
  167. this.ctx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight);
  168. };
  169. img.src = this.imgUrl + '?time=' + new Date().valueOf();
  170. this.canvasCopy.style.display = "block";
  171. },
  172. // 获取签名
  173. getSignature() {
  174. // canvas图片base64 转 File 对象
  175. var dataURL = this.canvas.toDataURL("image/jpg"),
  176. arr = dataURL.split(","),
  177. mime = arr[0].match(/:(.*?);/)[1],
  178. bstr = atob(arr[1]),
  179. n = bstr.length,
  180. u8arr = new Uint8Array(n);
  181. while (n--) {
  182. u8arr[n] = bstr.charCodeAt(n);
  183. }
  184. var obj = new Blob([u8arr], { type: mime }),
  185. time = new Date().toGMTString(),
  186. formData = new FormData();
  187. formData.append("file", obj);
  188. // 获取文件后缀
  189. var suffix = formData.get("file").type.split("/")[1];
  190. req
  191. .get("/carsource-api/upyun/sign", { suffix: suffix })
  192. .then(response => {
  193. if (response.data.code === 0) {
  194. this.uploadOption.path = response.data.data.path;
  195. formData.append("policy", response.data.data.policy);
  196. formData.append("authorization", response.data.data.signature);
  197. this.updateImg(formData);
  198. }
  199. })
  200. .catch(function(error) {});
  201. },
  202. // 上传
  203. updateImg(formData) {
  204. axios({
  205. url: "http://v0.api.upyun.com/tmp-img",
  206. method: "POST",
  207. data: formData
  208. }).then(response => {
  209. if (response.data.code == 200) {
  210. this.$message.success("图片修改成功");
  211. this.canvasClose("upload", response.data.url.slice(4));
  212. }
  213. });
  214. },
  215. // 裁剪框缩放 移动
  216. startResize(e, n) {
  217. this.resizeFX = n;
  218. $(document).mousemove(this.resizeDiv);
  219. document.addEventListener("mouseup", this.stopResize);
  220. },
  221. stopResize(e) {
  222. $(document).off("mousemove", this.resizeDiv);
  223. document.removeEventListener("mouseup", this.stopResize);
  224. },
  225. startMove(e) {
  226. this.movePrev = [e.pageX, e.pageY];
  227. $(document).mousemove(this.moveDiv);
  228. document.addEventListener("mouseup", this.stopMove);
  229. },
  230. stopMove(e) {
  231. $(document).off("mousemove", this.moveDiv);
  232. document.removeEventListener("mouseup", this.stopMove);
  233. },
  234. moveDiv(e) {
  235. // 马赛克
  236. if (this.isMa) {
  237. this.paintRect(e);
  238. }
  239. // 清除马赛克
  240. if (this.isMaClear) {
  241. this.paintRectClear(e);
  242. }
  243. // 裁剪
  244. if (this.isDrop) {
  245. var targetDiv = $("#canvas-mainBox"),
  246. offsetArr = targetDiv.offset();
  247. var chaX = e.pageX - this.movePrev[0],
  248. chaY = e.pageY - this.movePrev[1],
  249. ox = parseFloat(targetDiv.css("left")),
  250. oy = parseFloat(targetDiv.css("top"));
  251. targetDiv.css({
  252. left: ox + chaX + "px",
  253. top: oy + chaY + "px"
  254. });
  255. this.movePrev = [e.pageX, e.pageY];
  256. }
  257. },
  258. resizeDiv(e) {
  259. e.preventDefault();
  260. e.stopPropagation();
  261. // 获取需要改变尺寸元素到页面的距离
  262. var targetDiv = $("#canvas-mainBox"),
  263. offsetArr = targetDiv.offset();
  264. var eleSWidth = targetDiv.width(),
  265. eleSHeight = targetDiv.height(),
  266. ox = parseFloat(targetDiv.css("left")),
  267. oy = parseFloat(targetDiv.css("top"));
  268. // 获取鼠标位置,和元素初始offset进行对比,
  269. var chaX = e.pageX - offsetArr.left,
  270. chaY = e.pageY - offsetArr.top;
  271. switch (this.resizeFX) {
  272. case 0:
  273. //如果移动距离接近宽度或高度,则不进行改变
  274. if (chaX >= eleSWidth - 10 || chaY >= eleSHeight - 10) {
  275. return;
  276. }
  277. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  278. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  279. targetDiv.css({
  280. width: eleSWidth + chaX * -1 + "px",
  281. height: eleSHeight + chaY * -1 + "px",
  282. left: ox + chaX + "px",
  283. top: oy + chaY + "px"
  284. });
  285. break;
  286. case 1:
  287. //如果移动距离接近宽度或高度,则不进行改变
  288. if (chaY >= eleSHeight - 10) {
  289. return;
  290. }
  291. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  292. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  293. targetDiv.css({
  294. height: eleSHeight + chaY * -1 + "px",
  295. top: oy + chaY + "px"
  296. });
  297. break;
  298. case 2:
  299. //如果移动距离接近宽度或高度,则不进行改变
  300. if (chaX <= 10 || chaY >= eleSHeight - 10) {
  301. return;
  302. }
  303. // 获得位置差(m-e),先设置宽度和高度,设置位置
  304. // 原始高+((m-e)*-1),原始宽+((m-e)),原始位置+(m-e)
  305. targetDiv.css({
  306. width: chaX + "px",
  307. height: eleSHeight + chaY * -1 + "px",
  308. top: oy + chaY + "px"
  309. });
  310. break;
  311. case 3:
  312. //如果移动距离接近宽度或高度,则不进行改变
  313. if (chaX <= 10) {
  314. return;
  315. }
  316. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  317. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  318. targetDiv.css({
  319. width: chaX + "px"
  320. });
  321. break;
  322. case 4:
  323. //如果移动距离接近宽度或高度,则不进行改变
  324. if (chaX <= 10 || chaY <= 10) {
  325. return;
  326. }
  327. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  328. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  329. targetDiv.css({
  330. width: chaX + "px",
  331. height: chaY + "px"
  332. });
  333. break;
  334. case 5:
  335. //如果移动距离接近宽度或高度,则不进行改变
  336. if (chaY <= 10) {
  337. return;
  338. }
  339. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  340. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  341. targetDiv.css({
  342. height: chaY + "px"
  343. });
  344. break;
  345. case 6:
  346. //如果移动距离接近宽度或高度,则不进行改变
  347. if (chaX >= eleSWidth - 10 || chaY <= 10) {
  348. return;
  349. }
  350. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  351. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  352. targetDiv.css({
  353. width: eleSWidth + chaX * -1 + "px",
  354. height: chaY + "px",
  355. left: ox + chaX + "px"
  356. });
  357. break;
  358. case 7:
  359. //如果移动距离接近宽度或高度,则不进行改变
  360. if (chaX >= eleSWidth - 10) {
  361. return;
  362. }
  363. // 获得位置差(m-e),先设置宽度和高度,再设置位置
  364. // 原始宽高+((m-e)*-1),原始位置+(m-e)
  365. targetDiv.css({
  366. width: eleSWidth + chaX * -1 + "px",
  367. left: ox + chaX + "px"
  368. });
  369. break;
  370. default:
  371. break;
  372. }
  373. },
  374. // 裁剪
  375. clipReady() {
  376. this.btnIndex = 3;
  377. this.isMa = false;
  378. this.isDrop = true;
  379. this.isMaClear = false;
  380. },
  381. // 马赛克
  382. paintRectReady() {
  383. this.btnIndex = 1;
  384. this.isMa = true;
  385. this.isDrop = false;
  386. this.isMaClear = false;
  387. },
  388. // 橡皮擦
  389. paintRectClearReady() {
  390. this.btnIndex = 2;
  391. this.isMa = false;
  392. this.isDrop = false;
  393. this.isMaClear = true;
  394. },
  395. // 绘制马赛克
  396. paintRect(e) {
  397. var offT = this.canvas.offsetTop, // 距离上边距离
  398. offL = this.canvas.offsetLeft, // 距离左边距离
  399. x = e.clientX,
  400. y = e.clientY;
  401. if(this.mouseX - x > this.maSize/2 || x - this.mouseX > this.maSize/2 || this.mouseY - y > this.maSize/2 || y - this.mouseY > this.maSize/2){
  402. var oImg = this.ctx.getImageData(x - offL ,y - offT,this.maSize,this.maSize);
  403. var w = oImg.width;
  404. var h = oImg.height;
  405. //马赛克的程度,数字越大越模糊
  406. var num = 6;
  407. //等分画布
  408. var stepW = w/num;
  409. var stepH = h/num;
  410. //这里是循环画布的像素点
  411. for(var i=0;i<stepH;i++){
  412. for(var j=0;j<stepW;j++){
  413. //获取一个小方格的随机颜色,这是小方格的随机位置获取的
  414. var color = this.getXY(oImg,j*num+Math.floor(Math.random()*num),i*num+Math.floor(Math.random()*num));
  415. //这里是循环小方格的像素点,
  416. for(var k=0;k<num;k++){
  417. for(var l=0;l<num;l++){
  418. //设置小方格的颜色
  419. this.setXY(oImg,j*num+l,i*num+k,color);
  420. }
  421. }
  422. }
  423. }
  424. this.ctx.putImageData(oImg,x - offL ,y - offT);
  425. this.mouseX = e.clientX
  426. this.mouseY = e.clientY
  427. }
  428. },
  429. getXY(obj,x,y){
  430. var w = obj.width;
  431. var h = obj.height;
  432. var d = obj.data;
  433. var color = [];
  434. color[0] = d[4*(y*w+x)];
  435. color[1] = d[4*(y*w+x)+1];
  436. color[2] = d[4*(y*w+x)+2];
  437. color[3] = d[4*(y*w+x)+3];
  438. return color;
  439. },
  440. setXY(obj,x,y,color){
  441. var w = obj.width;
  442. var h = obj.height;
  443. var d = obj.data;
  444. d[4*(y*w+x)] = color[0];
  445. d[4*(y*w+x)+1] = color[1];
  446. d[4*(y*w+x)+2] = color[2];
  447. d[4*(y*w+x)+3] = color[3];
  448. },
  449. // 清除马赛克
  450. paintRectClear(e) {
  451. var offT = this.canvasCopy.offsetTop, // 距离上边距离
  452. offL = this.canvasCopy.offsetLeft, // 距离左边距离
  453. x = e.clientX,
  454. y = e.clientY,
  455. // 获取原图此位置图像数据
  456. imageData = this.ctxCopy.getImageData(
  457. x - offL,
  458. y - offT,
  459. this.maSize,
  460. this.maSize
  461. );
  462. this.ctx.putImageData(imageData, x - offL, y - offT);
  463. },
  464. // 关闭画布
  465. canvasClose(type, url) {
  466. this.$emit("isShowImgChange", type, url);
  467. }
  468. }
  469. };
  470. </script>
  471. <style scoped>
  472. .canvas-clip {
  473. position: fixed;
  474. top: 0;
  475. bottom: 0;
  476. left: 0;
  477. right: 0;
  478. z-index: 9010;
  479. background: #000;
  480. }
  481. .canvas-mainBox {
  482. position: absolute;
  483. width: 400px;
  484. height: 300px;
  485. left: 50%;
  486. top: 50%;
  487. margin-left: -200px;
  488. margin-top: -150px;
  489. border: 1px solid #FFF;
  490. cursor: move;
  491. z-index: 9009;
  492. }
  493. .canvas-minBox {
  494. position: absolute;
  495. width: 8px;
  496. height: 8px;
  497. background: #FFF;
  498. }
  499. .left-up {
  500. top: -4px;
  501. left: -4px;
  502. cursor: nw-resize;
  503. }
  504. .up {
  505. top: -4px;
  506. left: 50%;
  507. margin-left: -4px;
  508. cursor: n-resize;
  509. }
  510. .right-up {
  511. top: -4px;
  512. right: -4px;
  513. cursor: ne-resize;
  514. }
  515. .right {
  516. top: 50%;
  517. margin-top: -4px;
  518. right: -4px;
  519. cursor: e-resize;
  520. }
  521. .right-down {
  522. bottom: -4px;
  523. right: -4px;
  524. cursor: se-resize;
  525. }
  526. .down {
  527. bottom: -4px;
  528. left: 50%;
  529. margin-left: -4px;
  530. cursor: s-resize;
  531. }
  532. .left-down {
  533. bottom: -4px;
  534. left: -4px;
  535. cursor: sw-resize;
  536. }
  537. .left {
  538. top: 50%;
  539. margin-top: -4px;
  540. left: -4px;
  541. cursor: w-resize;
  542. }
  543. .canvas-btns {
  544. position: fixed;
  545. right: 50px;
  546. top: 30px;
  547. z-index: 9003;
  548. }
  549. .canvas-btns button {
  550. display: inline-blovk;
  551. background: green;
  552. cursor: pointer;
  553. border: none;
  554. width: 60px;
  555. height: 30px;
  556. line-height: 30px;
  557. color: #fff;
  558. font-size: 15px;
  559. }
  560. .canvas-btns button.active {
  561. background: rgb(32, 230, 32);
  562. }
  563. .canvas-btns button.close {
  564. background: rgb(230, 72, 32);
  565. }
  566. .canvas-copy {
  567. position: absolute;
  568. top: 50%;
  569. left: 50%;
  570. margin-top: -300px;
  571. margin-left: -400px;
  572. z-index: 9007;
  573. }
  574. .canvas-mosatic {
  575. position: absolute;
  576. top: 50%;
  577. left: 50%;
  578. margin-top: -300px;
  579. margin-left: -400px;
  580. z-index: 9009;
  581. }
  582. .canvas-area {
  583. position: absolute;
  584. top: 50%;
  585. left: 50%;
  586. margin-top: -300px;
  587. margin-left: -400px;
  588. z-index: 9008;
  589. }
  590. .paint-size{
  591. margin-top: 20px;
  592. font-size: 13px;
  593. color: #FFF;
  594. height: 30px;
  595. line-height: 30px;
  596. text-align: right;
  597. }
  598. .paint-size input{
  599. vertical-align: middle;
  600. background: green;
  601. }
  602. .paint-size .size-num{
  603. display: inline-block;
  604. width: 15px;
  605. }
  606. .hoverClear{
  607. cursor: url('./paint.png'),auto;
  608. }
  609. .hoverPaint{
  610. cursor: url('./paint.png'),auto;
  611. }
  612. </style>

4.效果图如下:

总结

以上所述是小编给大家介绍的基于Html5 canvas实现裁剪图片和马赛克功能及又拍云上传图片 功能 ,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对w3xue网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号