js-watermark

JavaScript 图片文字水印、图片图片水印生成工具,生成 base64 编码图片。

当前为 2024-07-19 提交的版本,查看 最新版本

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/452322/1413240/js-watermark.js

  1. /**
  2. * @js-watermark.js
  3. * @author WhiteSev
  4. * @Created: 22-09-26
  5. * @repository: https://github.com/WhiteSevs/js-watermark
  6. * @forked by:https://github.com/gisonyeung/js-watermark
  7. * @description JavaScript 图片文字水印、图片图片水印生成工具,生成 base64 编码图片。
  8. */
  9.  
  10. (function (global, factory) {
  11. /**
  12. * 不使用define
  13. * typeof define === "function" && define.amd
  14. * define(factory)
  15. */
  16. if (typeof exports === "object" && typeof module !== "undefined") {
  17. /* 适用于NodeJs或typeScript */
  18. module.exports = factory();
  19. } else {
  20. global = typeof globalThis !== "undefined" ? globalThis : global || self;
  21. /* 适用于浏览器中,且this对象是window,如果this是其它,那么会在其它对象下注册对象 */
  22. global.Watermark = factory(global.Watermark);
  23. }
  24. })(typeof window !== "undefined" ? window : this, function (AnotherWatermark) {
  25. "use strict";
  26. /**
  27. * @class
  28. */
  29. let Watermark = function () {};
  30. /**
  31. * @author zhangxinxu(.com)
  32. * @licence MIT
  33. * @description http://www.zhangxinxu.com/wordpress/?p=7362
  34. */
  35. /* api扩展-设置字符间距 */
  36. CanvasRenderingContext2D.prototype.letterSpacingText = function (
  37. text,
  38. x,
  39. y,
  40. letterSpacing
  41. ) {
  42. var context = this;
  43. var canvas = context.canvas;
  44.  
  45. if (!letterSpacing && canvas) {
  46. letterSpacing = parseFloat(window.getComputedStyle(canvas).letterSpacing);
  47. }
  48. if (!letterSpacing) {
  49. return this.fillText(text, x, y);
  50. }
  51.  
  52. var arrText = text.split("");
  53. var align = context.textAlign || "left";
  54.  
  55. /* 这里仅考虑水平排列 */
  56. var originWidth = context.measureText(text).width;
  57. /* 应用letterSpacing占据宽度 */
  58. var actualWidth = originWidth + letterSpacing * (arrText.length - 1);
  59. /* 根据水平对齐方式确定第一个字符的坐标 */
  60. if (align == "center") {
  61. x = x - actualWidth / 2;
  62. } else if (align == "right") {
  63. x = x - actualWidth;
  64. }
  65.  
  66. /* 临时修改为文本左对齐 */
  67. context.textAlign = "left";
  68. /* 开始逐字绘制 */
  69. arrText.forEach(function (letter) {
  70. var letterWidth = context.measureText(letter).width;
  71. context.fillText(letter, x, y);
  72. /* 确定下一个字符的横坐标 */
  73. x = x + letterWidth + letterSpacing;
  74. });
  75. /* 对齐方式还原 */
  76. context.textAlign = align;
  77. };
  78.  
  79. /* api扩展-自动换行 */
  80. CanvasRenderingContext2D.prototype.wrapText = function (
  81. text,
  82. x,
  83. y,
  84. maxWidth,
  85. lineHeight,
  86. stroke
  87. ) {
  88. if (
  89. typeof text != "string" ||
  90. typeof x != "number" ||
  91. typeof y != "number"
  92. ) {
  93. return;
  94. }
  95.  
  96. var context = this;
  97. var canvas = context.canvas;
  98.  
  99. if (typeof maxWidth == "undefined") {
  100. maxWidth = (canvas && canvas.width) || 300;
  101. }
  102. if (typeof lineHeight == "undefined") {
  103. lineHeight =
  104. (canvas && parseInt(window.getComputedStyle(canvas).lineHeight)) ||
  105. parseInt(window.getComputedStyle(document.body).lineHeight);
  106. }
  107.  
  108. /* 字符分隔为数组 */
  109. var arrText = text.split("");
  110. var line = "";
  111.  
  112. for (var n = 0; n < arrText.length; n++) {
  113. var testLine = line + arrText[n];
  114. var metrics = context.measureText(testLine);
  115. var testWidth = metrics.width;
  116. if (testWidth > maxWidth && n > 0) {
  117. if (stroke) {
  118. context.strokeText(line, x, y, canvas.width);
  119. } else {
  120. context.fillText(line, x, y);
  121. }
  122. line = arrText[n];
  123. y += lineHeight;
  124. } else {
  125. line = testLine;
  126. }
  127. }
  128. if (stroke) {
  129. context.strokeText(line, x, y, canvas.width);
  130. } else {
  131. context.fillText(line, x, y);
  132. }
  133. };
  134.  
  135. /* api扩展-垂直排列 */
  136. CanvasRenderingContext2D.prototype.fillTextVertical = function (text, x, y) {
  137. var context = this;
  138. var canvas = context.canvas;
  139.  
  140. var arrText = text.split("");
  141. var arrWidth = arrText.map(function (letter) {
  142. return context.measureText(letter).width;
  143. });
  144.  
  145. var align = context.textAlign;
  146. var baseline = context.textBaseline;
  147.  
  148. if (align == "left") {
  149. x = x + Math.max.apply(null, arrWidth) / 2;
  150. } else if (align == "right") {
  151. x = x - Math.max.apply(null, arrWidth) / 2;
  152. }
  153. if (
  154. baseline == "bottom" ||
  155. baseline == "alphabetic" ||
  156. baseline == "ideographic"
  157. ) {
  158. y = y - arrWidth[0] / 2;
  159. } else if (baseline == "top" || baseline == "hanging") {
  160. y = y + arrWidth[0] / 2;
  161. }
  162.  
  163. context.textAlign = "center";
  164. context.textBaseline = "middle";
  165.  
  166. /* 开始逐字绘制 */
  167. arrText.forEach(function (letter, index) {
  168. /* 确定下一个字符的纵坐标位置 */
  169. var letterWidth = arrWidth[index];
  170. /* 是否需要旋转判断 */
  171. var code = letter.charCodeAt(0);
  172. if (code <= 256) {
  173. context.translate(x, y);
  174. /* 英文字符,旋转90° */
  175. context.rotate((90 * Math.PI) / 180);
  176. context.translate(-x, -y);
  177. } else if (index > 0 && text.charCodeAt(index - 1) < 256) {
  178. /* y修正 */
  179. y = y + arrWidth[index - 1] / 2;
  180. }
  181. context.fillText(letter, x, y);
  182. /* 旋转坐标系还原成初始态 */
  183. context.setTransform(1, 0, 0, 1, 0, 0);
  184. /* 确定下一个字符的纵坐标位置 */
  185. var letterWidth = arrWidth[index];
  186. y = y + letterWidth;
  187. });
  188. /* 水平垂直对齐方式还原 */
  189. context.textAlign = align;
  190. context.textBaseline = baseline;
  191. };
  192.  
  193. /**
  194. * 加载File对象
  195. *
  196. * @param {object} file
  197. * @async
  198. */
  199. function loadFile(file) {
  200. let fileReader = new FileReader();
  201. return new Promise((res) => {
  202. fileReader.onloadend = async function (e) {
  203. res(e);
  204. };
  205. fileReader.readAsDataURL(file);
  206. });
  207. }
  208.  
  209. /**
  210. * 加载Image对象
  211. *
  212. * @param {?string} src
  213. * @async
  214. */
  215. function loadImage(src) {
  216. let image = new Image();
  217.  
  218. return new Promise((res) => {
  219. image.onload = () => {
  220. res(image);
  221. };
  222. image.src = src;
  223. });
  224. }
  225. /**
  226. * 检查坐标是否重复
  227. * @param {Array} arrayData
  228. * @param {number} x
  229. * @param {number} y
  230. * @returns {boolean}
  231. */
  232. function checkInArrayByPos(arrayData, x, y) {
  233. let flag = false;
  234. Array.from(arrayData).forEach((item) => {
  235. if (item["x"] == x && item["y"] == y) {
  236. flag = true;
  237. return;
  238. }
  239. });
  240. return flag;
  241. }
  242. /**
  243. * 获取文字占据的宽度,高度
  244. * @param {string} char
  245. * @param {object} style
  246. * @returns {{height:Number,width:Number}}
  247. */
  248. function getCharSizeByCanvas(char, style = {}) {
  249. let textCanvas = document.createElement("canvas");
  250. textCanvas.style.positon = "ablsolute";
  251. let textCTX = textCanvas.getContext("2d");
  252. let { fontSize = 14, fontFamily = "Microsoft Yahei" } = style;
  253. document.body.appendChild(textCanvas);
  254. textCTX.font = `${fontSize}px ${fontFamily}`;
  255. document.body.removeChild(textCanvas);
  256. let text = textCTX.measureText(char); /* TextMetrics object */
  257. textCTX.fillText(char, 50, 50);
  258. let result = {
  259. height: parseInt(fontSize),
  260. width: parseInt(text.width),
  261. };
  262. return result;
  263. }
  264. /**
  265. * 获取随机值
  266. * @param {Array} arr
  267. */
  268. function getRandValue(arr) {
  269. if (arr instanceof Array) {
  270. return arr[Math.floor(Math.random() * arr.length)];
  271. } else {
  272. return arr;
  273. }
  274. }
  275. /**
  276. * 通过 file 对象载入图片文件-异步
  277. * @async
  278. * @param {object} file
  279. */
  280. Watermark.prototype.setFile = function (file) {
  281. let that = this;
  282. return new Promise(async (res) => {
  283. var fileReader = await loadFile(file);
  284. await that.setImage(fileReader.target.result);
  285. res(true);
  286. });
  287. };
  288. /**
  289. * 通过 base64 载入图片文件-异步
  290. * @param {string} src
  291. * @async
  292. */
  293. Watermark.prototype.setImage = function (src) {
  294. this.dataUrl = src;
  295. let that = this;
  296. return new Promise(async (res) => {
  297. var image = await loadImage(src);
  298. that.sizes = {
  299. width: image.width,
  300. height: image.height,
  301. };
  302.  
  303. var canvas = document.createElement("canvas");
  304.  
  305. canvas.width = that.sizes.width;
  306. canvas.height = that.sizes.height;
  307. var ctx = canvas.getContext("2d");
  308.  
  309. ctx.drawImage(image, 0, 0);
  310. image = null;
  311. that.canvas = canvas;
  312. res(true);
  313. });
  314. };
  315.  
  316. /**
  317. * 获取是否存在图片对象
  318. * @returns {boolean}
  319. */
  320. Watermark.prototype.hasImage = function () {
  321. return !!this.dataUrl;
  322. };
  323.  
  324. /**
  325. * 获取当前图片尺寸
  326. * @returns {number}
  327. */
  328. Watermark.prototype.getSize = function () {
  329. return this.sizes;
  330. };
  331.  
  332. /**
  333. * 清空水印
  334. */
  335. Watermark.prototype.clearMark = function () {
  336. let that = this;
  337. if (typeof that.canvas === "undefined") {
  338. return;
  339. }
  340.  
  341. function _clearMark_() {
  342. var ctx = that.canvas.getContext("2d");
  343. /* 清空画布 */
  344. ctx.clearRect(0, 0, that.canvas.width, that.canvas.height);
  345. var w = that.canvas.width;
  346. var h = that.canvas.height;
  347. that.canvas.width = w;
  348. that.canvas.height = h;
  349. /* 清除path路径 */
  350. ctx.beginPath();
  351. /* 重绘 */
  352. var image = new Image();
  353. image.src = that.dataUrl;
  354. ctx.drawImage(image, 0, 0);
  355. image = null;
  356. }
  357. _clearMark_();
  358. };
  359.  
  360. /**
  361. * 添加文字水印(全屏)
  362. * @param {object} opts
  363. */
  364. Watermark.prototype.addText = function (opts) {
  365. var options = {
  366. text: ["Call By waterMark.addText"],
  367. fontSize: "6vw",
  368. fontFamily: "Microsoft Yahei",
  369. color: "#000000",
  370. textAlign: "center",
  371. /* 描边 */
  372. stroke: false,
  373. globalAlpha: 0.7,
  374. /* -360 ~ 360 */
  375. rotateAngle: 50,
  376. /* 必须大于0 */
  377. maxWidth: 100,
  378. /* 必须大于0 */
  379. xMoveDistance: 30,
  380. /* 必须大于0 */
  381. yMoveDistance: 30,
  382. };
  383. for (let key in options) {
  384. if (typeof opts[key] !== "undefined") {
  385. options[key] = opts[key];
  386. }
  387. }
  388. options.maxWidth = parseInt(options.maxWidth) > 0 ? options.maxWidth : 1;
  389. options.xMoveDistance =
  390. parseInt(options.xMoveDistance) > 0 ? options.xMoveDistance : 1;
  391. options.yMoveDistance =
  392. parseInt(options.yMoveDistance) > 0 ? options.yMoveDistance : 1;
  393. var ctx = this.canvas.getContext("2d");
  394.  
  395. var fontSize = options.fontSize;
  396. fontSize = fontSize.toString();
  397. /* 转换 vw */
  398. if (~fontSize.indexOf("vw")) {
  399. fontSize = ((this.sizes.width / 100) * parseInt(fontSize)).toFixed(0);
  400. }
  401. fontSize = parseInt(fontSize);
  402.  
  403. /* 绘制水印 */
  404. ctx.font = fontSize + "px " + options.fontFamily;
  405. ctx.fillStyle = options.color;
  406. ctx.textAlign = options.textAlign;
  407. ctx.globalAlpha = options.globalAlpha; /* 透明度 */
  408.  
  409. let canvasWidth = this.sizes.width,
  410. /* 画布宽高 */
  411. canvasHeight = this.sizes.height;
  412. let rotateAngle = (options.rotateAngle * Math.PI) / 180;
  413. let xMoveDistance = options.xMoveDistance; /* 水平移动距离 */
  414. let yMoveDistance = options.yMoveDistance; /* 垂直移动距离 */
  415. let maxWidth = options.maxWidth; /* 文字最大宽度 */
  416. let lineHeight = fontSize; /* 文字占据高度 */
  417. let pos = [];
  418. for (var i = canvasWidth / 2; i < canvasWidth; i += xMoveDistance) {
  419. /* 右侧铺满 */
  420. for (var j = canvasHeight / 2; j < canvasHeight; j += yMoveDistance) {
  421. /* 右下 */
  422. if (!checkInArrayByPos(pos, i, j)) {
  423. pos = pos.concat({
  424. x: i,
  425. y: j,
  426. });
  427. ctx.setTransform(1, 0, 0, 1, 0, 0);
  428. ctx.translate(i, j);
  429. ctx.rotate(rotateAngle);
  430. ctx.wrapText(
  431. getRandValue(options.text),
  432. 0,
  433. 0,
  434. maxWidth,
  435. lineHeight,
  436. options.stroke
  437. );
  438. }
  439. }
  440. for (var k = canvasHeight / 2; k > 0; k -= yMoveDistance) {
  441. /* 右上 */
  442. if (!checkInArrayByPos(pos, i, k)) {
  443. pos = pos.concat({
  444. x: i,
  445. y: k,
  446. });
  447. ctx.setTransform(1, 0, 0, 1, 0, 0);
  448. ctx.translate(i, k);
  449. ctx.rotate(rotateAngle);
  450. ctx.wrapText(
  451. getRandValue(options.text),
  452. 0,
  453. 0,
  454. maxWidth,
  455. lineHeight,
  456. options.stroke
  457. );
  458. }
  459. }
  460. }
  461.  
  462. for (var i = canvasWidth / 2; i > 0; i -= xMoveDistance) {
  463. /* 左侧铺满 */
  464. for (var j = canvasHeight / 2; j < canvasHeight; j += yMoveDistance) {
  465. /* 左下 */
  466. if (!checkInArrayByPos(pos, i, j)) {
  467. pos = pos.concat({
  468. x: i,
  469. y: j,
  470. });
  471. ctx.setTransform(1, 0, 0, 1, 0, 0);
  472. ctx.translate(i, j);
  473. ctx.rotate(rotateAngle);
  474. ctx.wrapText(
  475. getRandValue(options.text),
  476. 0,
  477. 0,
  478. maxWidth,
  479. lineHeight,
  480. options.stroke
  481. );
  482. }
  483. }
  484. for (var k = canvasHeight / 2; k > 0; k -= yMoveDistance) {
  485. /* 左上 */
  486. if (!checkInArrayByPos(pos, i, k)) {
  487. pos = pos.concat({
  488. x: i,
  489. y: k,
  490. });
  491. ctx.setTransform(1, 0, 0, 1, 0, 0);
  492. ctx.translate(i, k);
  493. ctx.rotate(rotateAngle);
  494. ctx.wrapText(
  495. getRandValue(options.text),
  496. 0,
  497. 0,
  498. maxWidth,
  499. lineHeight,
  500. options.stroke
  501. );
  502. }
  503. }
  504. }
  505. };
  506.  
  507. /**
  508. * 添加像素文字水印(单个)
  509. * @param {object} opts
  510. */
  511. Watermark.prototype.addPixelText = function (opts) {
  512. var options = {
  513. text: "像素文字水印",
  514. /* 像素文字 */
  515. big: {
  516. fontSize: 150,
  517. fontFamily: "微软雅黑",
  518. textAlign: "center",
  519. rotateAngle: 0,
  520. /* 描边 */
  521. stroke: false,
  522. },
  523. /* 绘制像素的文字 */
  524. small: {
  525. fontSize: 10,
  526. fontFamily: "微软雅黑",
  527. color: "#000",
  528. textAlign: "center",
  529. globalAlpha: 0.7,
  530. },
  531. };
  532. for (let key in options) {
  533. if (typeof opts[key] !== "undefined") {
  534. options[key] = opts[key];
  535. }
  536. }
  537. var ctx = this.canvas.getContext("2d");
  538. var tmpCanvas = document.createElement("canvas");
  539. var tmpctx = tmpCanvas.getContext("2d");
  540. tmpCanvas.width = this.sizes.width;
  541. tmpCanvas.height = this.sizes.height;
  542. tmpctx.font = options.big.fontSize + "px " + options.big.fontFamily;
  543. tmpctx.textAlign = options.big.textAlign;
  544. tmpctx.textBaseline = "middle";
  545. tmpctx.translate(tmpCanvas.width / 2, tmpCanvas.height / 2);
  546. tmpctx.rotate((options.big.rotateAngle * Math.PI) / 180);
  547. tmpctx.translate(-tmpCanvas.width / 2, -tmpCanvas.height / 2);
  548. if (options.big.stroke) {
  549. tmpctx.strokeText(
  550. options.text,
  551. tmpCanvas.width / 2,
  552. tmpCanvas.height / 2,
  553. tmpCanvas.width
  554. );
  555. } else {
  556. tmpctx.fillText(options.text, tmpCanvas.width / 2, tmpCanvas.height / 2);
  557. }
  558.  
  559. var textArray = options.text.split("");
  560. var textPixleInfo = tmpctx.getImageData(
  561. 0,
  562. 0,
  563. tmpCanvas.width,
  564. tmpCanvas.height
  565. );
  566. var pixelArray = [];
  567. for (var i = 0; i < tmpCanvas.height; i += options.small.fontSize) {
  568. for (var j = 0; j < tmpCanvas.width; j += options.small.fontSize) {
  569. var index = j + i * tmpCanvas.width;
  570. var a = textPixleInfo.data[index * 4 + 3];
  571. if (a > 128) {
  572. //存入数组
  573. pixelArray.push({
  574. text: getRandValue(textArray),
  575. x: j,
  576. y: i,
  577. });
  578. }
  579. }
  580. }
  581.  
  582. ctx.font = options.small.fontSize + "px " + options.small.fontFamily;
  583. ctx.fillStyle = options.small.color;
  584. ctx.textAlign = options.small.textAlign;
  585. ctx.textBaseline = "middle";
  586. ctx.globalAlpha = options.small.globalAlpha;
  587. pixelArray.forEach((item) => {
  588. ctx.fillText(item.text, item.x, item.y);
  589. });
  590. };
  591.  
  592. /**
  593. * 添加图片水印(全屏)
  594. * @param {object} opts
  595. * @returns
  596. */
  597. Watermark.prototype.addImage = function (opts) {
  598. if (opts.imageArray == null) {
  599. alert("参数缺少imageArray");
  600. return false;
  601. }
  602. if (opts.imageArray.length === 0) {
  603. alert("参数imageArray不能为空");
  604. return false;
  605. }
  606. let options = {
  607. imageArray: [],
  608. /* 里面为水印Image对象 */
  609. width: 50,
  610. /* 必须大于0 */
  611. height: 50,
  612. /* 必须大于0 */
  613. globalAlpha: 0.5,
  614. rotateAngle: 0,
  615. xMoveDistance: 70,
  616. /* 必须大于0 */
  617. yMoveDistance: 70,
  618. /* 必须大于0 */
  619. };
  620. for (let key in options) {
  621. if (typeof opts[key] !== "undefined") {
  622. options[key] = opts[key];
  623. }
  624. }
  625. options.width = parseInt(options.width) > 0 ? options.width : 1;
  626. options.height = parseInt(options.height) > 0 ? options.height : 1;
  627. options.xMoveDistance =
  628. parseInt(options.xMoveDistance) > 0 ? options.xMoveDistance : 1;
  629. options.yMoveDistance =
  630. parseInt(options.yMoveDistance) > 0 ? options.yMoveDistance : 1;
  631. let ctx = this.canvas.getContext("2d");
  632.  
  633. let waterImageCanvasArray = [];
  634. let waterImageCanvasDiagonal = parseInt(
  635. Math.sqrt(options.width * options.width + options.height * options.height)
  636. ); /* 水印对角线 */
  637.  
  638. let canvasWidth = this.sizes.width,
  639. /* 画布宽高 */
  640. canvasHeight = this.sizes.height;
  641. let rotateAngle = (options.rotateAngle * Math.PI) / 180; /* 旋转角度 */
  642. let xMoveDistance = options.xMoveDistance; /* 水平移动距离 */
  643. let yMoveDistance = options.yMoveDistance; /* 垂直移动距离 */
  644.  
  645. let centerDrawLeftPosX =
  646. canvasWidth / 2 -
  647. waterImageCanvasDiagonal / 2; /* 中心的绘制水印的左上角坐标x */
  648. let centerDrawLeftPosY =
  649. canvasHeight / 2 -
  650. waterImageCanvasDiagonal / 2; /* 绘制水印的左上角坐标y */
  651. let waterDrawPosX =
  652. (waterImageCanvasDiagonal - options.width) / 2; /* 水印里图片坐标x */
  653. let waterDrawPosY =
  654. (waterImageCanvasDiagonal - options.height) / 2; /* 水印里图片坐标y */
  655.  
  656. Array.from(options.imageArray).forEach((item) => {
  657. /* 先把水印绘制好 */
  658. var waterImageCanvas = document.createElement("canvas");
  659. var waterctx = waterImageCanvas.getContext("2d");
  660.  
  661. waterImageCanvas.width = waterImageCanvasDiagonal;
  662. waterImageCanvas.height = waterImageCanvasDiagonal;
  663. waterctx.globalAlpha = options.globalAlpha; /* 透明度 */
  664. waterctx.translate(
  665. waterImageCanvasDiagonal / 2,
  666. waterImageCanvasDiagonal / 2
  667. );
  668. waterctx.rotate(rotateAngle);
  669. waterctx.translate(
  670. -waterImageCanvasDiagonal / 2,
  671. -waterImageCanvasDiagonal / 2
  672. );
  673. waterctx.drawImage(
  674. item,
  675. waterDrawPosX,
  676. waterDrawPosY,
  677. options.width,
  678. options.height
  679. );
  680.  
  681. waterImageCanvasArray = waterImageCanvasArray.concat(waterImageCanvas);
  682. });
  683.  
  684. function randomArrayData(array_data) {
  685. /* 随机项 */
  686. return array_data[Math.floor(Math.random() * array_data.length)];
  687. }
  688. ctx.setTransform(1, 0, 0, 1, 0, 0);
  689. let pos = [];
  690. for (let i = centerDrawLeftPosX; i < canvasWidth; i += xMoveDistance) {
  691. /* 右侧铺满 */
  692. for (let j = centerDrawLeftPosY; j < canvasHeight; j += yMoveDistance) {
  693. /* 右下 */
  694. if (!checkInArrayByPos(pos, i, j)) {
  695. pos = pos.concat({
  696. x: i,
  697. y: j,
  698. });
  699. ctx.drawImage(
  700. randomArrayData(waterImageCanvasArray),
  701. i,
  702. j
  703. ); /* 绘制水印 */
  704. }
  705. }
  706. for (
  707. let k = centerDrawLeftPosY;
  708. k > -Math.abs(waterImageCanvasDiagonal);
  709. k -= yMoveDistance
  710. ) {
  711. /* 右上 */
  712. if (!checkInArrayByPos(pos, i, k)) {
  713. pos = pos.concat({
  714. x: i,
  715. y: k,
  716. });
  717. ctx.drawImage(randomArrayData(waterImageCanvasArray), i, k);
  718. }
  719. }
  720. }
  721. for (
  722. let i = centerDrawLeftPosX;
  723. i > -Math.abs(waterImageCanvasDiagonal);
  724. i -= xMoveDistance
  725. ) {
  726. /* 左侧铺满 */
  727. for (let j = centerDrawLeftPosY; j < canvasHeight; j += yMoveDistance) {
  728. /* 左下 */
  729. if (!checkInArrayByPos(pos, i, j)) {
  730. pos = pos.concat({
  731. x: i,
  732. y: j,
  733. });
  734. ctx.drawImage(randomArrayData(waterImageCanvasArray), i, j);
  735. }
  736. }
  737. for (
  738. let k = centerDrawLeftPosY;
  739. k > -Math.abs(waterImageCanvasDiagonal);
  740. k -= yMoveDistance
  741. ) {
  742. /* 左上 */
  743. if (!checkInArrayByPos(pos, i, k)) {
  744. pos = pos.concat({
  745. x: i,
  746. y: k,
  747. });
  748. ctx.drawImage(randomArrayData(waterImageCanvasArray), i, k);
  749. }
  750. }
  751. }
  752. };
  753.  
  754. /**
  755. * 获得原图
  756. * @returns {?string}
  757. */
  758. Watermark.prototype.getPreview = function () {
  759. return this.dataUrl;
  760. };
  761.  
  762. /**
  763. * 绘制图片
  764. * @param {string} type png|jpeg
  765. * @returns
  766. */
  767. Watermark.prototype.render = function (type) {
  768. type = type === "png" ? "png" : "jpeg";
  769. return this.canvas.toDataURL("image/" + type);
  770. };
  771.  
  772. /**
  773. * 绘制图片Blob Url-异步
  774. * @async
  775. */
  776. Watermark.prototype.renderBlob = function () {
  777. let that = this;
  778. return new Promise((res) => {
  779. that.canvas.toBlob(function (blob) {
  780. res(window.URL.createObjectURL(blob));
  781. });
  782. });
  783. };
  784.  
  785. /**
  786. * 释放控制权
  787. * @returns {Watermark}
  788. */
  789. Watermark.prototype.noConflict = function () {
  790. if (window.Watermark) {
  791. delete window.Watermark;
  792. }
  793. if (AnotherWatermark) {
  794. window.Watermark = AnotherWatermark;
  795. }
  796. return Watermark;
  797. };
  798. return Watermark;
  799. });