UNPKG

node-puzzle

Version:

Node.js 生成滑块验证码的拼图和背景图。

104 lines (101 loc) 3.78 kB
import { getRandomPoints, drawPuzzle } from 'create-puzzle'; import { randomInt } from 'ut2'; import { loadImage, createCanvas } from '@napi-rs/canvas'; async function createPuzzle(input, options = {}) { const { // 拼图 borderWidth = 1, borderColor = 'rgba(255,255,255,0.7)', fillColor = 'rgba(255,255,255,0.7)', points: outPoints, width = 60, height = 60, x: outX, y: outY, margin = 2, equalHeight = true, format = 'png', quality = 80, avifConfig, // 背景图 bgWidth: outBgWidth, bgHeight: outBgHeight, bgOffset = [0, 0], bgFormat = 'jpeg', bgQuality: outBgQuality, bgAvifConfig: outBgAvifConfig } = options; const bgQuality = outBgQuality || quality; const bgAvifConfig = outBgAvifConfig || avifConfig; const originImg = await loadImage(input); // 拼图点 const points = typeof outPoints === 'number' || !outPoints ? getRandomPoints(outPoints) : outPoints; const bgWidth = typeof outBgWidth === 'number' && outBgWidth > 0 ? outBgWidth > width ? outBgWidth : width : originImg.width; const bgHeight = typeof outBgHeight === 'number' && outBgHeight > 0 ? outBgHeight > height ? outBgHeight : height : originImg.height; const maxOffsetX = bgWidth - width; const maxOffsetY = bgHeight - height; let x = typeof outX === 'undefined' ? randomInt(width, maxOffsetX) : outX || 0; let y = typeof outY === 'undefined' ? randomInt(0, maxOffsetY) : outY || 0; if (x < 0) { x = 0; } else if (x > maxOffsetX) { x = maxOffsetX; } if (y < 0) { y = 0; } else if (y > maxOffsetY) { y = maxOffsetY; } // 背景图 const bgCanvas = createCanvas(bgWidth, bgHeight); const bgCtx = bgCanvas.getContext('2d'); bgCtx.clearRect(0, 0, bgWidth, bgHeight); bgCtx.drawImage(originImg, bgOffset[0], bgOffset[1], bgWidth, bgHeight, 0, 0, bgWidth, bgHeight); // 拼图 const puzzleCanvasHeight = equalHeight ? bgHeight : height; const puzzleY = equalHeight ? y : 0; const puzzleCanvas = createCanvas(width, puzzleCanvasHeight); const puzzleCtx = puzzleCanvas.getContext('2d'); puzzleCtx.strokeStyle = borderColor; puzzleCtx.lineWidth = borderWidth; puzzleCtx.clearRect(0, 0, width, puzzleCanvasHeight); drawPuzzle(puzzleCtx, { x: 0, y: puzzleY, w: width, h: height, points, margin }); puzzleCtx.clip(); puzzleCtx.drawImage(bgCanvas, x, y, width, height, 0, puzzleY, width, height); // 背景图添加遮罩 const maskCanvas = createCanvas(width, height); const maskCtx = maskCanvas.getContext('2d'); maskCtx.clearRect(0, 0, width, height); maskCtx.fillStyle = fillColor; maskCtx.fillRect(0, 0, width, height); bgCtx.strokeStyle = borderColor; bgCtx.lineWidth = borderWidth; drawPuzzle(bgCtx, { x, y, w: width, h: height, points, margin }); bgCtx.clip(); bgCtx.drawImage(maskCanvas, x, y, width, height); const puzzleCanvasToData = format === 'png' ? puzzleCanvas.encode(format) : format === 'avif' ? puzzleCanvas.encode(format, avifConfig) : puzzleCanvas.encode(format, quality); const bgCanvasToData = bgFormat === 'png' ? bgCanvas.encode(bgFormat) : bgFormat === 'avif' ? bgCanvas.encode(bgFormat, bgAvifConfig) : bgCanvas.encode(bgFormat, bgQuality); // export canvas as image const [puzzle, bg] = await Promise.all([puzzleCanvasToData, bgCanvasToData]); return { bg, puzzle, x, y: equalHeight ? 0 : y }; } export { createPuzzle as default };