From f7863ef77c530b07467b9dc13d41a4dbb44d9628 Mon Sep 17 00:00:00 2001 From: strawberry <2806566736@.qq.com> Date: Sat, 12 Jul 2025 23:01:38 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BE=8A=E7=9A=AE=E7=BA=B8=E4=B9=A6=E9=A1=B5?= =?UTF-8?q?=E5=A4=A7=E5=B9=85=E9=87=8D=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- project/plugins.js | 638 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 618 insertions(+), 20 deletions(-) diff --git a/project/plugins.js b/project/plugins.js index 6e356ff..0d1e136 100644 --- a/project/plugins.js +++ b/project/plugins.js @@ -22128,35 +22128,569 @@ let time=0 this.pagemax = 1 this.page = 0 - } - paperTexture() { + + } + paperTexture(dir) { const textureCanvas = document.createElement('canvas'); textureCanvas.width = 325; textureCanvas.height = 400; const textureCtx = textureCanvas.getContext('2d'); - // 填充浅色背景 - textureCtx.fillStyle = '#f8f4e6'; - textureCtx.fillRect(0, 0, 325, 400); + // 生成羊皮纸基础色(更精细的暖色调范围) + const baseHue = 45; // 35-60黄色到橙色范围 + const baseSaturation = 40; // 25-50%饱和度 + const baseLightness = 85; // 80-95%亮度 - // 添加纸张纹理 + // 1. 创建高度自然的基底纹理 + const createOrganicBase = () => { + // 基础底色(轻微噪点纹理) + textureCtx.fillStyle = `hsl(${baseHue}, ${baseSaturation}%, ${baseLightness}%)`; + textureCtx.fillRect(0, 0, 325, 400); - for (let i = 0; i < 1000; i++) { - const x = Math.random() * 325; - const y = Math.random() * 400; - const radius = Math.random() * 2; - const alpha = Math.random() * 0.1 - textureCtx.beginPath(); - textureCtx.arc(x, y, radius, 0, Math.PI * 2); - textureCtx.fillStyle = `rgba(100, 80, 60, ${alpha})`; - textureCtx.fill(); + // 生成有机噪点纹理(模拟羊皮纸纤维) + const noiseData = textureCtx.createImageData(325, 400); + for (let i = 0; i < noiseData.data.length; i += 4) { + // Perlin噪声生成更自然的纹理 + const x = (i / 4) % 325; + const y = Math.floor((i / 4) / 325); + const noiseVal = this.perlinNoise(x / 50, y / 50) * 0.5 + + this.perlinNoise(x / 20, y / 20) * 0.3 + + this.perlinNoise(x / 5, y / 5) * 0.2; + + // 基于噪声调整颜色 + const hueVar = noiseVal * 10; + const satVar = noiseVal * 15; + const lightVar = noiseVal * 8; + + noiseData.data[i] = this.HSLtoRGB( + (baseHue + hueVar) % 360, + Math.max(10, Math.min(70, baseSaturation + satVar)), + Math.max(70, Math.min(98, baseLightness + lightVar)) + ).r; + noiseData.data[i + 1] = this.HSLtoRGB( + (baseHue + hueVar) % 360, + Math.max(10, Math.min(70, baseSaturation + satVar)), + Math.max(70, Math.min(98, baseLightness + lightVar)) + ).g; + noiseData.data[i + 2] = this.HSLtoRGB( + (baseHue + hueVar) % 360, + Math.max(10, Math.min(70, baseSaturation + satVar)), + Math.max(70, Math.min(98, baseLightness + lightVar)) + ).b; + noiseData.data[i + 3] = 255; + } + textureCtx.putImageData(noiseData, 0, 0); + + // 添加手工染色效果(不规则色斑) + /*for (let i = 0; i < 8 + Math.random() * 5; i++) { + const centerX = Math.random() * 325; + const centerY = Math.random() * 400; + const radius = 30 + Math.random() * 70; + const hueVar = (Math.random() - 0.5) * 15; + const satVar = (Math.random() - 0.5) * 20; + const lightVar = (Math.random() - 0.5) * 12; + + // 创建有机形状的渐变 + const gradient = textureCtx.createRadialGradient( + centerX, centerY, 0, + centerX + (Math.random() - 0.5) * 20, + centerY + (Math.random() - 0.5) * 20, + radius * (0.8 + Math.random() * 0.4) + ); + + gradient.addColorStop(0, `hsla( + ${baseHue + hueVar}, + ${baseSaturation + satVar}%, + ${baseLightness + lightVar}%, + ${0.2 + Math.random() * 0.3} + )`); + gradient.addColorStop(1, 'hsla(0, 0%, 0%, 0)'); + + textureCtx.fillStyle = gradient; + textureCtx.beginPath(); + // 有机形状绘制(非正圆) + const points = 12 + Math.floor(Math.random() * 8); + textureCtx.moveTo( + centerX + radius * Math.cos(0), + centerY + radius * Math.sin(0) + ); + for (let j = 1; j <= points; j++) { + const angle = (j / points) * Math.PI * 2; + const dist = radius * (0.7 + Math.random() * 0.6); + textureCtx.lineTo( + centerX + dist * Math.cos(angle), + centerY + dist * Math.sin(angle) + ); + } + textureCtx.closePath(); + textureCtx.fill(); + }*/ + }; + createOrganicBase(); + // 更自然的边缘破损效果 + const addEdgeTears = () => { + const tearCount = Math.floor(Math.random() * 4) + 2; + + for (let i = 0; i < tearCount; i++) { + const size = 5 + Math.random() * 12; + const roughness = 2 + Math.random() * 3; + + if (dir === "left") { + // 左侧边缘破损 + if (Math.random() > 0.3) { + const y = Math.random() * 350 + 25; + createOrganicTear(0, y, size, Math.PI / 2, roughness); + } else { + const x = Math.random() * 50; + const y = Math.random() > 0.5 ? 0 : 400; + createOrganicTear(x, y, size * 0.7, Math.random() > 0.5 ? 0 : Math.PI, roughness); + } + } else { + // 右侧边缘破损 + if (Math.random() > 0.3) { + const y = Math.random() * 350 + 25; + createOrganicTear(325, y, size, -Math.PI / 2, roughness); + } else { + const x = 275 + Math.random() * 50; + const y = Math.random() > 0.5 ? 0 : 400; + createOrganicTear(x, y, size * 0.7, Math.random() > 0.5 ? 0 : Math.PI, roughness); + } + } + } + + function createOrganicTear(x, y, size, angle, roughness) { + const points = []; + const steps = 10 + Math.floor(Math.random() * 10); + + // 生成有机形状的点 + for (let i = 0; i <= steps; i++) { + const t = i / steps; + const r = size * (0.8 + Math.random() * 0.4); + const a = angle + Math.PI + t * Math.PI; + const noiseX = (Math.random() * 2 - 1) * roughness; + const noiseY = (Math.random() * 2 - 1) * roughness; + + points.push({ + x: x + r * Math.cos(a) * (0.5 + t * 0.5) + noiseX, + y: y + r * Math.sin(a) * (0.5 + t * 0.5) + noiseY + }); + } + + // 绘制破损形状 + textureCtx.beginPath(); + textureCtx.moveTo(x, y); + points.forEach(p => textureCtx.lineTo(p.x, p.y)); + textureCtx.closePath(); + + // 破损内部阴影 + textureCtx.fillStyle = `rgba(140, 110, 80, ${0.08 + Math.random()*0.07})`; + textureCtx.fill(); + + // 破损边缘线 + textureCtx.lineWidth = 0.3 + Math.random() * 0.4; + textureCtx.strokeStyle = `rgba(100, 80, 60, ${0.4 + Math.random()*0.2})`; + textureCtx.stroke(); + + // 添加一些细节线条 + for (let j = 0; j < 2; j++) { + textureCtx.beginPath(); + const detailX = x + (points[0].x - x) * 0.3 * (j + 1); + const detailY = y + (points[0].y - y) * 0.3 * (j + 1); + textureCtx.moveTo(detailX, detailY); + + for (let k = 1; k < points.length; k++) { + const p = points[k]; + const toX = x + (p.x - x) * 0.3 * (j + 1); + const toY = y + (p.y - y) * 0.3 * (j + 1); + textureCtx.lineTo(toX, toY); + } + + textureCtx.lineWidth = 0.1 + Math.random() * 0.2; + textureCtx.strokeStyle = `rgba(120, 90, 70, ${0.2 + Math.random()*0.1})`; + textureCtx.stroke(); + } + } + }; + addEdgeTears(); + // 2. 完美四角阴影系统 + /** + * 根据方向调整边缘阴影强度 + * @param {string} dir 方向参数('left'或'right') + */ + const addMasterfulEdgeShadows = (dir) => { + const shadowHue = baseHue - 8; + const shadowSaturation = baseSaturation + 15; + const shadowLightness = baseLightness * 0.55; + const shadowSize = 35 + Math.random() * 15; + + const shadowCanvas = document.createElement('canvas'); + shadowCanvas.width = 325; + shadowCanvas.height = 400; + const shadowCtx = shadowCanvas.getContext('2d'); + + // 计算每个像素的阴影强度 + const gradientData = shadowCtx.createImageData(325, 400); + for (let i = 0; i < gradientData.data.length; i += 4) { + const x = (i / 4) % 325; + const y = Math.floor((i / 4) / 325); + + // 基础距离计算 + const leftDist = x / shadowSize; + const rightDist = (325 - x) / shadowSize; + const topDist = y / shadowSize; + const bottomDist = (400 - y) / shadowSize; + + // 根据方向调整左右阴影强度 + let leftShadowIntensity = 0.7; + let rightShadowIntensity = 0.7; + + if (dir === "left") { + rightShadowIntensity *= 0.6; // 削弱右侧阴影 + } else if (dir === "right") { + leftShadowIntensity *= 0.6; // 削弱左侧阴影 + } + + // 计算角部距离 + const tlDist = Math.sqrt(x * x + y * y) / (shadowSize * 1.4); + const trDist = Math.sqrt((325 - x) * (325 - x) + y * y) / (shadowSize * 1.4); + const blDist = Math.sqrt(x * x + (400 - y) * (400 - y)) / (shadowSize * 1.4); + const brDist = Math.sqrt((325 - x) * (325 - x) + (400 - y) * (400 - y)) / (shadowSize * 1.4); + + // 综合阴影强度 + let shadowAlpha = 0; + + // 左侧阴影计算 + if (leftDist < 1) { + shadowAlpha = Math.max(shadowAlpha, leftShadowIntensity * (1 - leftDist)); + } + + // 右侧阴影计算 + if (rightDist < 1) { + shadowAlpha = Math.max(shadowAlpha, rightShadowIntensity * (1 - rightDist)); + } + + // 顶部和底部阴影(保持不变) + if (topDist < 1) { + shadowAlpha = Math.max(shadowAlpha, 0.7 * (1 - topDist)); + } + if (bottomDist < 1) { + shadowAlpha = Math.max(shadowAlpha, 0.7 * (1 - bottomDist)); + } + + // 角部阴影(取相邻两边强度的中间值) + if (tlDist < 1) { + const cornerAlpha = (leftShadowIntensity + 0.7) / 2 * (1 - tlDist); + shadowAlpha = Math.max(shadowAlpha, cornerAlpha); + } + if (trDist < 1) { + const cornerAlpha = (rightShadowIntensity + 0.7) / 2 * (1 - trDist); + shadowAlpha = Math.max(shadowAlpha, cornerAlpha); + } + if (blDist < 1) { + const cornerAlpha = (leftShadowIntensity + 0.7) / 2 * (1 - blDist); + shadowAlpha = Math.max(shadowAlpha, cornerAlpha); + } + if (brDist < 1) { + const cornerAlpha = (rightShadowIntensity + 0.7) / 2 * (1 - brDist); + shadowAlpha = Math.max(shadowAlpha, cornerAlpha); + } + + // 限制最大透明度 + shadowAlpha = Math.min(0.6, shadowAlpha); + + // 转换为RGB + const rgb = this.HSLtoRGB(shadowHue, shadowSaturation, shadowLightness); + + gradientData.data[i] = rgb.r; + gradientData.data[i + 1] = rgb.g; + gradientData.data[i + 2] = rgb.b; + gradientData.data[i + 3] = shadowAlpha * 255; + } + + shadowCtx.putImageData(gradientData, 0, 0); + + // 应用阴影 + textureCtx.globalCompositeOperation = 'multiply'; + textureCtx.drawImage(shadowCanvas, 0, 0); + textureCtx.globalCompositeOperation = 'source-over'; } + addMasterfulEdgeShadows(dir); + + // 3. 超真实细节系统 + const addHyperRealisticDetails = () => { + // 3.1 纤维纹理(模拟羊皮纸纤维) + for (let i = 0; i < 2000; i++) { + const x = Math.random() * 325; + const y = Math.random() * 400; + const length = 1 + Math.random() * 4; + const angle = Math.random() * Math.PI * 2; + const lightVar = (Math.random() > 0.5 ? 5 : -5) * (0.5 + Math.random()); + + textureCtx.beginPath(); + textureCtx.moveTo(x, y); + textureCtx.lineTo( + x + Math.cos(angle) * length, + y + Math.sin(angle) * length + ); + + textureCtx.strokeStyle = `hsla( + ${baseHue + (Math.random() - 0.5) * 8}, + ${baseSaturation * (0.7 + Math.random() * 0.6)}%, + ${baseLightness + lightVar}%, + ${0.03 + Math.random() * 0.04} + )`; + textureCtx.lineWidth = 0.2 + Math.random() * 0.3; + textureCtx.stroke(); + } + + // 3.2 自然划痕系统 + for (let i = 0; i < 10 + Math.random() * 10; i++) { + const scratchLength = 15 + Math.random() * 60; + const startX = Math.random() * 325; + const startY = Math.random() * 400; + const angle = Math.random() * Math.PI * 2; + const curveIntensity = 5 + Math.random() * 10; + + // 主划痕路径 + textureCtx.beginPath(); + textureCtx.moveTo(startX, startY); + + // 贝塞尔曲线控制点 + const cp1x = startX + Math.cos(angle) * scratchLength * 0.3 + (Math.random() - 0.5) * curveIntensity; + const cp1y = startY + Math.sin(angle) * scratchLength * 0.3 + (Math.random() - 0.5) * curveIntensity; + const cp2x = startX + Math.cos(angle) * scratchLength * 0.7 + (Math.random() - 0.5) * curveIntensity; + const cp2y = startY + Math.sin(angle) * scratchLength * 0.7 + (Math.random() - 0.5) * curveIntensity; + const endX = startX + Math.cos(angle) * scratchLength; + const endY = startY + Math.sin(angle) * scratchLength; + + textureCtx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY); + + // 划痕样式(宽度变化) + const lineWidth = 0.1 + Math.random() * 0.4; + const scratchDarkness = 0.6 + Math.random() * 0.3; + + textureCtx.strokeStyle = `hsla( + ${baseHue - 5 + Math.random() * 10}, + ${baseSaturation + 5 + Math.random() * 15}%, + ${baseLightness * scratchDarkness}%, + ${0.4 + Math.random() * 0.3} + )`; + textureCtx.lineWidth = lineWidth; + textureCtx.lineCap = 'round'; + textureCtx.stroke(); + + // 划痕高光(模拟凹陷) + if (Math.random() > 0.3) { + textureCtx.beginPath(); + textureCtx.moveTo( + startX + (Math.random() - 0.5) * lineWidth * 2, + startY + (Math.random() - 0.5) * lineWidth * 2 + ); + textureCtx.bezierCurveTo( + cp1x + (Math.random() - 0.5) * lineWidth * 2, + cp1y + (Math.random() - 0.5) * lineWidth * 2, + cp2x + (Math.random() - 0.5) * lineWidth * 2, + cp2y + (Math.random() - 0.5) * lineWidth * 2, + endX + (Math.random() - 0.5) * lineWidth * 2, + endY + (Math.random() - 0.5) * lineWidth * 2 + ); + + textureCtx.strokeStyle = `hsla( + ${baseHue + 5 + Math.random() * 5}, + ${baseSaturation * 0.7}%, + ${baseLightness * 1.1}%, + ${0.2 + Math.random() * 0.1} + )`; + textureCtx.lineWidth = lineWidth * 0.7; + textureCtx.stroke(); + } + } + + // 3.3 专业级污渍系统 + for (let i = 0; i < 3 + Math.random() * 5; i++) { + const stainType = Math.floor(Math.random() * 3); // 0: 液体污渍, 1: 指纹, 2: 老化斑点 + + + if (stainType === 1) { + // 指纹污渍 + const centerX = Math.random() * 325; + const centerY = Math.random() * 400; + const size = 10 + Math.random() * 15; + const rotation = Math.random() * Math.PI * 2; + + // 指纹纹路 + for (let j = 0; j < 15 + Math.random() * 10; j++) { + const radius = size * (0.3 + Math.random() * 0.7); + const angle = (j / 15) * Math.PI * 2 + rotation; + const width = size * (0.03 + Math.random() * 0.05); + + textureCtx.beginPath(); + textureCtx.arc( + centerX + Math.cos(angle) * radius * 0.5, + centerY + Math.sin(angle) * radius * 0.5, + width, 0, Math.PI * 2 + ); + + textureCtx.fillStyle = `hsla( + ${baseHue - 5 + Math.random() * 10}, + ${baseSaturation + 5}%, + ${baseLightness * 0.8}%, + ${0.1 + Math.random() * 0.05} + )`; + textureCtx.fill(); + } + } else { + // 老化斑点 + const centerX = Math.random() * 325; + const centerY = Math.random() * 400; + const size = 5 + Math.random() * 15; + const points = 8 + Math.floor(Math.random() * 6); + + textureCtx.beginPath(); + textureCtx.moveTo( + centerX + size * Math.cos(0), + centerY + size * Math.sin(0) + ); + + for (let j = 1; j <= points; j++) { + const angle = (j / points) * Math.PI * 2; + const radius = size * (0.7 + Math.random() * 0.6); + textureCtx.lineTo( + centerX + radius * Math.cos(angle), + centerY + radius * Math.sin(angle) + ); + } + textureCtx.closePath(); + + const gradient = textureCtx.createRadialGradient( + centerX, centerY, 0, + centerX, centerY, size * 1.2 + ); + gradient.addColorStop(0, `hsla( + ${baseHue - 15 + Math.random() * 20}, + ${baseSaturation + 20}%, + ${baseLightness * 0.5}%, + ${0.2 + Math.random() * 0.1} + )`); + gradient.addColorStop(1, 'hsla(0, 0%, 0%, 0)'); + + textureCtx.fillStyle = gradient; + textureCtx.fill(); + } + } + }; + addHyperRealisticDetails(); return textureCanvas; } + + // 辅助函数(需要在类中定义) + /** + * 2D Perlin噪声生成器 + * @param {number} x x坐标 + * @param {number} y y坐标 + * @returns {number} 噪声值(-1到1之间) + */ + perlinNoise(x, y) { + // 梯度向量表 + const grad3 = [ + [1, 1, 0], + [-1, 1, 0], + [1, -1, 0], + [-1, -1, 0], + [1, 0, 1], + [-1, 0, 1], + [1, 0, -1], + [-1, 0, -1], + [0, 1, 1], + [0, -1, 1], + [0, 1, -1], + [0, -1, -1] + ]; + + // 置换表(随机排列0-255) + if (!this.perm) { + this.perm = new Array(512); + for (let i = 0; i < 256; i++) { + this.perm[i] = this.perm[i + 256] = Math.floor(Math.random() * 256); + } + } + + // 辅助函数 + const dot = (g, x, y) => g[0] * x + g[1] * y; + const fade = t => t * t * t * (t * (t * 6 - 15) + 10); + const lerp = (a, b, t) => a + t * (b - a); + + // 确定单元方块 + const X = Math.floor(x) & 255; + const Y = Math.floor(y) & 255; + + // 相对单元方块的坐标 + x -= Math.floor(x); + y -= Math.floor(y); + + // 计算四个角落的梯度贡献 + const n00 = dot(grad3[this.perm[X + this.perm[Y]] % 12], x, y); + const n01 = dot(grad3[this.perm[X + this.perm[Y + 1]] % 12], x, y - 1); + const n10 = dot(grad3[this.perm[X + 1 + this.perm[Y]] % 12], x - 1, y); + const n11 = dot(grad3[this.perm[X + 1 + this.perm[Y + 1]] % 12], x - 1, y - 1); + + // 使用缓和曲线插值 + const u = fade(x); + const v = fade(y); + + return lerp( + lerp(n00, n10, u), + lerp(n01, n11, u), + v + ); + } + + /** + * HSL颜色空间转RGB + * @param {number} h 色相(0-360) + * @param {number} s 饱和度(0-100) + * @param {number} l 亮度(0-100) + * @returns {Object} {r, g, b} 各通道值(0-255) + */ + HSLtoRGB(h, s, l) { + h /= 360; + s /= 100; + l /= 100; + + let r, g, b; + + if (s === 0) { + r = g = b = l; // 灰度 + } else { + const hue2rgb = (p, q, t) => { + if (t < 0) t += 1; + if (t > 1) t -= 1; + if (t < 1 / 6) return p + (q - p) * 6 * t; + if (t < 1 / 2) return q; + if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; + return p; + }; + + const q = l < 0.5 ? l * (1 + s) : l + s - l * s; + const p = 2 * l - q; + + r = hue2rgb(p, q, h + 1 / 3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1 / 3); + } + + return { + r: Math.round(r * 255), + g: Math.round(g * 255), + b: Math.round(b * 255) + }; + } background() { book.style.display = "block" + this.leftbackground = this.paperTexture('left') + this.rightbackground = this.paperTexture('right') if (core.domStyle.isVertical) { //对竖屏进行绘制坐标变换,只需要考虑横屏绘制,竖屏将自适应转置 core.maps._setHDCanvasSize(ctx, 416, 676) ctx.save(); //保存设置 @@ -22184,11 +22718,42 @@ let time=0 ctx.lineTo(676 - dx, 0) ctx.lineTo(676, dy) ctx.closePath() - ctx.fillStyle = "#e0d6c2" ctx.fill() - - core.drawImage(ctx, this.paperTexture(), 0, 0, 325, 400, dx, 0, this.width, this.height) - core.drawImage(ctx, this.paperTexture(), 0, 0, 325, 400, dx + this.width, 0, this.width, this.height) + ctx.strokeStyle = `rgb(${248*0.7}, ${244*0.7}, ${230*0.7})`; + ctx.beginPath() + ctx.moveTo(2, 100) + ctx.lineTo(2, 130) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(5, 20) + ctx.lineTo(5, 30) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(6, 240) + ctx.lineTo(6, 260) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(10, 340) + ctx.lineTo(10, 360) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(665, 100) + ctx.lineTo(665, 130) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(670, 190) + ctx.lineTo(670, 160) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(671, 20) + ctx.lineTo(671, 130) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(673, 350) + ctx.lineTo(673, 370) + ctx.stroke() + core.drawImage(ctx, this.leftbackground, 0, 0, 325, 400, dx, 0, this.width, this.height) + core.drawImage(ctx, this.rightbackground, 0, 0, 325, 400, dx + this.width, 0, this.width, this.height) ctx.beginPath() ctx.moveTo(0, 416) @@ -22204,8 +22769,41 @@ let time=0 ctx.lineTo(338, this.height) ctx.lineTo(338 + dx, 416) ctx.closePath() - ctx.fillStyle = '#706b61' ctx.fill() + ctx.beginPath() + ctx.moveTo(20, 402) + ctx.lineTo(50, 402) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(180, 405) + ctx.lineTo(220, 405) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(40, 409) + ctx.lineTo(80, 409) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(268, 412) + ctx.lineTo(290, 412) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(430, 403) + ctx.lineTo(470, 403) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(510, 407) + ctx.lineTo(530, 407) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(350, 414) + ctx.lineTo(370, 414) + ctx.stroke() + ctx.beginPath() + ctx.moveTo(602, 411) + ctx.lineTo(610, 411) + ctx.stroke() + + ctx.restore(); //恢复变换前的坐标,否则将连续转置 } }