您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
JavaScript 图片文字水印、图片图片水印生成工具,生成 base64 编码图片。
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/452322/1099857/js-watermark.js
/** * @js-watermark.js * @author WhiteSev * @Created: 22-09-26 * @repository: https://github.com/WhiteSevs/js-watermark * @forked by:https://github.com/gisonyeung/js-watermark * @description JavaScript 图片文字水印、图片图片水印生成工具,生成 base64 编码图片。 */ (function (global, factory) { if (typeof define === 'function' && (define.amd || define.cmd)) { define(factory); } else { global.Watermark = factory(); } }(this, function () { 'use strict'; /** * @author zhangxinxu(.com) * @licence MIT * @description http://www.zhangxinxu.com/wordpress/?p=7362 */ /* api扩展-设置字符间距 */ CanvasRenderingContext2D.prototype.letterSpacingText = function (text, x, y, letterSpacing) { var context = this; var canvas = context.canvas; if (!letterSpacing && canvas) { letterSpacing = parseFloat(window.getComputedStyle(canvas).letterSpacing); } if (!letterSpacing) { return this.fillText(text, x, y); } var arrText = text.split(''); var align = context.textAlign || 'left'; /* 这里仅考虑水平排列 */ var originWidth = context.measureText(text).width; /* 应用letterSpacing占据宽度 */ var actualWidth = originWidth + letterSpacing * (arrText.length - 1); /* 根据水平对齐方式确定第一个字符的坐标 */ if (align == 'center') { x = x - actualWidth / 2; } else if (align == 'right') { x = x - actualWidth; } /* 临时修改为文本左对齐 */ context.textAlign = 'left'; /* 开始逐字绘制 */ arrText.forEach(function (letter) { var letterWidth = context.measureText(letter).width; context.fillText(letter, x, y); /* 确定下一个字符的横坐标 */ x = x + letterWidth + letterSpacing; }); /* 对齐方式还原 */ context.textAlign = align; }; /* api扩展-自动换行 */ CanvasRenderingContext2D.prototype.wrapText = function (text, x, y, maxWidth, lineHeight) { if (typeof text != 'string' || typeof x != 'number' || typeof y != 'number') { return; } var context = this; var canvas = context.canvas; if (typeof maxWidth == 'undefined') { maxWidth = (canvas && canvas.width) || 300; } if (typeof lineHeight == 'undefined') { lineHeight = (canvas && parseInt(window.getComputedStyle(canvas).lineHeight)) || parseInt(window.getComputedStyle(document.body).lineHeight); } /* 字符分隔为数组 */ var arrText = text.split(''); var line = ''; for (var n = 0; n < arrText.length; n++) { var testLine = line + arrText[n]; var metrics = context.measureText(testLine); var testWidth = metrics.width; if (testWidth > maxWidth && n > 0) { context.fillText(line, x, y); line = arrText[n]; y += lineHeight; } else { line = testLine; } } context.fillText(line, x, y); }; /* api扩展-垂直排列 */ CanvasRenderingContext2D.prototype.fillTextVertical = function (text, x, y) { var context = this; var canvas = context.canvas; var arrText = text.split(''); var arrWidth = arrText.map(function (letter) { return context.measureText(letter).width; }); var align = context.textAlign; var baseline = context.textBaseline; if (align == 'left') { x = x + Math.max.apply(null, arrWidth) / 2; } else if (align == 'right') { x = x - Math.max.apply(null, arrWidth) / 2; } if (baseline == 'bottom' || baseline == 'alphabetic' || baseline == 'ideographic') { y = y - arrWidth[0] / 2; } else if (baseline == 'top' || baseline == 'hanging') { y = y + arrWidth[0] / 2; } context.textAlign = 'center'; context.textBaseline = 'middle'; /* 开始逐字绘制 */ arrText.forEach(function (letter, index) { /* 确定下一个字符的纵坐标位置 */ var letterWidth = arrWidth[index]; /* 是否需要旋转判断 */ var code = letter.charCodeAt(0); if (code <= 256) { context.translate(x, y); /* 英文字符,旋转90° */ context.rotate(90 * Math.PI / 180); context.translate(-x, -y); } else if (index > 0 && text.charCodeAt(index - 1) < 256) { /* y修正 */ y = y + arrWidth[index - 1] / 2; } context.fillText(letter, x, y); /* 旋转坐标系还原成初始态 */ context.setTransform(1, 0, 0, 1, 0, 0); /* 确定下一个字符的纵坐标位置 */ var letterWidth = arrWidth[index]; y = y + letterWidth; }); /* 水平垂直对齐方式还原 */ context.textAlign = align; context.textBaseline = baseline; }; /* 加载file对象 */ function loadFile(file) { let fileReader = new FileReader(); return new Promise(res => { fileReader.onloadend = async function (e) { res(e); }; fileReader.readAsDataURL(file); }) } /* 加载Image对象 */ function loadImage(src) { let image = new Image(); return new Promise(res => { image.onload = () => { res(image); }; image.src = src; }) } /* 检查坐标是否重复 */ function checkInArrayByPos(arrayData, x, y) { let flag = false; Array.from(arrayData).forEach(item => { if (item["x"] == x && item["y"] == y) { flag = true; return } }); return flag; } /* 获取文字占据的宽度,高度 */ function getCharSizeByCanvas(char, style = {}) { let textCanvas = document.createElement('canvas'); textCanvas.style.positon = "ablsolute"; let textCTX = textCanvas.getContext('2d'); let { fontSize = 14, fontFamily = "Microsoft Yahei" } = style document.body.appendChild(textCanvas) textCTX.font = `${fontSize}px ${fontFamily}` document.body.removeChild(textCanvas) let text = textCTX.measureText(char) /* TextMetrics object */ textCTX.fillText(char, 50, 50) let result = { height: parseInt(fontSize), width: parseInt(text.width) } return result } var Watermark = function () {}; /* 通过 file 对象载入图片文件-异步 */ Watermark.prototype.setFile = function (file) { let self = this; return new Promise(async res => { var fileReader = await loadFile(file); await self.setImage(fileReader.target.result); res(true); }) } /* 通过 base64 载入图片文件-异步 */ Watermark.prototype.setImage = function (src) { this.dataUrl = src; let self = this; return new Promise(async res => { var image = await loadImage(src); self.sizes = { width: image.width, height: image.height }; var canvas = document.createElement('canvas'); canvas.width = self.sizes.width; canvas.height = self.sizes.height; var ctx = canvas.getContext('2d'); ctx.drawImage(image, 0, 0); image = null; self.canvas = canvas; res(true); }) } /* 获取是否存在图片对象 */ Watermark.prototype.hasImage = function () { return !!this.dataUrl; } /* 获取当前图片尺寸 */ Watermark.prototype.getSize = function () { return this.sizes; } /* 清空水印 */ Watermark.prototype.clearMark = function () { let self = this; if (typeof self.canvas === "undefined") { return } function _clearMark_() { var ctx = self.canvas.getContext('2d'); /* 清空画布 */ ctx.clearRect(0, 0, self.canvas.width, self.canvas.height); var w = self.canvas.width; var h = self.canvas.height; self.canvas.width = w; self.canvas.height = h; /* 清除path路径 */ ctx.beginPath(); /* 重绘 */ var image = new Image(); image.src = self.dataUrl; ctx.drawImage(image, 0, 0); image = null; } _clearMark_(); } /* 增加文字水印(全屏) */ Watermark.prototype.addText = function (opts) { var options = { text: ['Call By waterMark.addText'], fontSize: '6vw', fontFamily: 'Microsoft Yahei', color: "#000000", textAlign: "center", globalAlpha: 0.7, rotateAngle: 50, /* -360 ~ 360 */ maxWidth: 100,/* 必须大于0 */ xMoveDistance: 30,/* 必须大于0 */ yMoveDistance: 30/* 必须大于0 */ }; for (let key in options) { if (typeof opts[key] !== "undefined") { options[key] = opts[key]; } } options.maxWidth = parseInt(options.maxWidth) > 0 ? options.maxWidth: 1; options.xMoveDistance = parseInt(options.xMoveDistance) > 0 ? options.xMoveDistance: 1; options.yMoveDistance = parseInt(options.yMoveDistance) > 0 ? options.yMoveDistance: 1; var ctx = this.canvas.getContext('2d'); var fontSize = options.fontSize; fontSize = fontSize.toString(); /* 转换 vw */ if (~fontSize.indexOf('vw')) { fontSize = (this.sizes.width / 100 * parseInt(fontSize)).toFixed(0); } fontSize = parseInt(fontSize); /* 绘制水印 */ ctx.font = fontSize + "px " + options.fontFamily; ctx.fillStyle = options.color; ctx.textAlign = options.textAlign; ctx.globalAlpha = options.globalAlpha; /* 透明度 */ let canvasWidth = this.sizes.width, /* 画布宽高 */ canvasHeight = this.sizes.height; let rotateAngle = options.rotateAngle * Math.PI / 180; let xMoveDistance = options.xMoveDistance; /* 水平移动距离 */ let yMoveDistance = options.yMoveDistance; /* 垂直移动距离 */ let maxWidth = options.maxWidth; /* 文字最大宽度 */ let lineHeight = fontSize; /* 文字占据高度 */ let pos = []; for (var i = canvasWidth / 2; i < canvasWidth; i += xMoveDistance) { /* 右侧铺满 */ for (var j = canvasHeight / 2; j < canvasHeight; j += yMoveDistance) { /* 右下 */ if (!checkInArrayByPos(pos, i, j)) { pos = pos.concat({ "x": i, "y": j }); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.translate(i, j); ctx.rotate(rotateAngle); ctx.wrapText(options.text[Math.floor(Math.random() * options.text.length)], 0, 0, maxWidth, lineHeight); } } for (var k = canvasHeight / 2; k > 0; k -= yMoveDistance) { /* 右上 */ if (!checkInArrayByPos(pos, i, k)) { pos = pos.concat({ "x": i, "y": k }); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.translate(i, k); ctx.rotate(rotateAngle); ctx.wrapText(options.text[Math.floor(Math.random() * options.text.length)], 0, 0, maxWidth, lineHeight); } } } for (var i = canvasWidth / 2; i > 0; i -= xMoveDistance) { /* 左侧铺满 */ for (var j = canvasHeight / 2; j < canvasHeight; j += yMoveDistance) { /* 左下 */ if (!checkInArrayByPos(pos, i, j)) { pos = pos.concat({ "x": i, "y": j }); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.translate(i, j); ctx.rotate(rotateAngle); ctx.wrapText(options.text[Math.floor(Math.random() * options.text.length)], 0, 0, maxWidth, lineHeight); } } for (var k = canvasHeight / 2; k > 0; k -= yMoveDistance) { /* 左上 */ if (!checkInArrayByPos(pos, i, k)) { pos = pos.concat({ "x": i, "y": k }); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.translate(i, k); ctx.rotate(rotateAngle); ctx.wrapText(options.text[Math.floor(Math.random() * options.text.length)], 0, 0, maxWidth, lineHeight); } } } } /* 添加图片水印(全屏) */ Watermark.prototype.addImage = function (opts) { if (opts.imageArray == null) { alert("参数缺少imageArray"); return false; } if (opts.imageArray.length === 0) { alert("参数imageArray不能为空"); return false; } let options = { imageArray: [], /* 里面为水印Image对象 */ width: 50, /* 必须大于0 */ height: 50,/* 必须大于0 */ globalAlpha: 0.5, rotateAngle: 0, xMoveDistance: 70,/* 必须大于0 */ yMoveDistance: 70,/* 必须大于0 */ } for (let key in options) { if (typeof opts[key] !== "undefined") { options[key] = opts[key]; } } options.width = parseInt(options.width) > 0 ? options.width: 1; options.height = parseInt(options.height) > 0 ? options.height: 1; options.xMoveDistance = parseInt(options.xMoveDistance) > 0 ? options.xMoveDistance: 1; options.yMoveDistance = parseInt(options.yMoveDistance) > 0 ? options.yMoveDistance: 1; let ctx = this.canvas.getContext('2d'); let waterImageCanvasArray = []; let waterImageCanvasDiagonal = parseInt(Math.sqrt(options.width * options.width + options.height * options.height)); /* 水印对角线 */ let canvasWidth = this.sizes.width, /* 画布宽高 */ canvasHeight = this.sizes.height; let rotateAngle = options.rotateAngle * Math.PI / 180; /* 旋转角度 */ let xMoveDistance = options.xMoveDistance; /* 水平移动距离 */ let yMoveDistance = options.yMoveDistance; /* 垂直移动距离 */ let centerDrawLeftPosX = canvasWidth / 2 - waterImageCanvasDiagonal / 2; /* 中心的绘制水印的左上角坐标x */ let centerDrawLeftPosY = canvasHeight / 2 - waterImageCanvasDiagonal / 2; /* 绘制水印的左上角坐标y */ let waterDrawPosX = (waterImageCanvasDiagonal - options.width) / 2; /* 水印里图片坐标x */ let waterDrawPosY = (waterImageCanvasDiagonal - options.height) / 2; /* 水印里图片坐标y */ Array.from(options.imageArray).forEach(item => { /* 先把水印绘制好 */ var waterImageCanvas = document.createElement("canvas"); var waterctx = waterImageCanvas.getContext("2d"); waterImageCanvas.width = waterImageCanvasDiagonal; waterImageCanvas.height = waterImageCanvasDiagonal; waterctx.globalAlpha = options.globalAlpha; /* 透明度 */ waterctx.translate(waterImageCanvasDiagonal / 2, waterImageCanvasDiagonal / 2); waterctx.rotate(rotateAngle); waterctx.translate(-waterImageCanvasDiagonal / 2, -waterImageCanvasDiagonal / 2); waterctx.drawImage(item, waterDrawPosX, waterDrawPosY, options.width, options.height); waterImageCanvasArray = waterImageCanvasArray.concat(waterImageCanvas); }) function randomArrayData(array_data) { /* 随机项 */ return array_data[Math.floor(Math.random() * array_data.length)]; } ctx.setTransform(1, 0, 0, 1, 0, 0); let pos = []; for (let i = centerDrawLeftPosX; i < canvasWidth; i += xMoveDistance) { /* 右侧铺满 */ for (let j = centerDrawLeftPosY; j < canvasHeight; j += yMoveDistance) { /* 右下 */ if (!checkInArrayByPos(pos, i, j)) { pos = pos.concat({ "x": i, "y": j }); ctx.drawImage(randomArrayData(waterImageCanvasArray), i, j); /* 绘制水印 */ } } for (let k = centerDrawLeftPosY; k > -Math.abs(waterImageCanvasDiagonal); k -= yMoveDistance) { /* 右上 */ if (!checkInArrayByPos(pos, i, k)) { pos = pos.concat({ "x": i, "y": k }); ctx.drawImage(randomArrayData(waterImageCanvasArray), i, k); } } } for (let i = centerDrawLeftPosX; i > -Math.abs(waterImageCanvasDiagonal); i -= xMoveDistance) { /* 左侧铺满 */ for (let j = centerDrawLeftPosY; j < canvasHeight; j += yMoveDistance) { /* 左下 */ if (!checkInArrayByPos(pos, i, j)) { pos = pos.concat({ "x": i, "y": j }); ctx.drawImage(randomArrayData(waterImageCanvasArray), i, j); } } for (let k = centerDrawLeftPosY; k > -Math.abs(waterImageCanvasDiagonal); k -= yMoveDistance) { /* 左上 */ if (!checkInArrayByPos(pos, i, k)) { pos = pos.concat({ "x": i, "y": k }); ctx.drawImage(randomArrayData(waterImageCanvasArray), i, k); } } } } /* 获得原图 */ Watermark.prototype.getPreview = function () { return this.dataUrl; } /* 绘制图片 */ Watermark.prototype.render = function (type) { type = type === 'png' ? 'png' : 'jpeg'; return this.canvas.toDataURL("image/" + type); }; /* 绘制图片Blob Url-异步 */ Watermark.prototype.renderBlob = function () { let self = this; return new Promise( res=>{ self.canvas.toBlob(function(blob){ res(window.URL.createObjectURL(blob)); }) }) }; return Watermark; }));
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址