|
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
引言
在Web前端开发中,图形绘制是一项基础且重要的技能。矩形作为最基础的几何图形之一,在各种Web应用中都有广泛的应用,从简单的界面元素到复杂的数据可视化。掌握JavaScript输出矩形的多种方法,不仅能提升你的前端开发技能,还能为你在创建用户界面、数据可视化、游戏开发等方面提供强大的工具。本文将带你从基础到高级,全面了解使用JavaScript绘制矩形的各种方法和技巧。
1. 基础方法:使用HTML和CSS创建矩形
1.1 使用HTML元素创建矩形
最简单的创建矩形的方法是使用HTML元素,如div,并通过CSS设置其样式:
- <div class="rectangle"></div>
复制代码- .rectangle {
- width: 200px;
- height: 100px;
- background-color: #3498db;
- }
复制代码
这种方法简单直观,适合创建静态的矩形元素,但缺乏动态性和灵活性。
1.2 使用JavaScript动态创建矩形元素
我们可以使用JavaScript动态创建矩形元素,并添加到DOM中:
- // 创建矩形元素
- function createRectangle(width, height, color) {
- const rectangle = document.createElement('div');
- rectangle.style.width = width + 'px';
- rectangle.style.height = height + 'px';
- rectangle.style.backgroundColor = color;
-
- // 添加到页面中
- document.body.appendChild(rectangle);
-
- return rectangle;
- }
- // 使用示例
- const myRectangle = createRectangle(300, 150, '#e74c3c');
复制代码
这种方法允许我们根据需要动态创建矩形,但仍然受限于CSS样式的能力。
2. Canvas API绘制矩形
Canvas API是HTML5提供的一个强大的绘图接口,它允许我们使用JavaScript在画布上绘制图形,包括矩形。
2.1 Canvas基础设置
首先,我们需要在HTML中创建一个canvas元素:
- <canvas id="myCanvas" width="500" height="300"></canvas>
复制代码
然后,在JavaScript中获取canvas上下文:
- const canvas = document.getElementById('myCanvas');
- const ctx = canvas.getContext('2d');
复制代码
2.2 绘制填充矩形
使用fillRect()方法绘制填充矩形:
- function drawFilledRectangle(x, y, width, height, color) {
- ctx.fillStyle = color;
- ctx.fillRect(x, y, width, height);
- }
- // 使用示例
- drawFilledRectangle(50, 50, 200, 100, '#3498db');
复制代码
2.3 绘制描边矩形
使用strokeRect()方法绘制只有边框的矩形:
- function drawStrokedRectangle(x, y, width, height, color, lineWidth) {
- ctx.strokeStyle = color;
- ctx.lineWidth = lineWidth || 1;
- ctx.strokeRect(x, y, width, height);
- }
- // 使用示例
- drawStrokedRectangle(100, 100, 200, 100, '#e74c3c', 3);
复制代码
2.4 绘制填充和描边矩形
结合fillRect()和strokeRect()方法,我们可以创建既有填充又有描边的矩形:
- function drawRectangleWithStroke(x, y, width, height, fillColor, strokeColor, lineWidth) {
- // 绘制填充矩形
- ctx.fillStyle = fillColor;
- ctx.fillRect(x, y, width, height);
-
- // 绘制描边矩形
- ctx.strokeStyle = strokeColor;
- ctx.lineWidth = lineWidth || 1;
- ctx.strokeRect(x, y, width, height);
- }
- // 使用示例
- drawRectangleWithStroke(150, 150, 200, 100, '#2ecc71', '#27ae60', 3);
复制代码
2.5 使用路径绘制矩形
我们也可以使用路径来绘制矩形,这提供了更多的灵活性:
- function drawRectangleWithPath(x, y, width, height, fillColor, strokeColor, lineWidth) {
- ctx.beginPath();
- ctx.rect(x, y, width, height);
-
- if (fillColor) {
- ctx.fillStyle = fillColor;
- ctx.fill();
- }
-
- if (strokeColor) {
- ctx.strokeStyle = strokeColor;
- ctx.lineWidth = lineWidth || 1;
- ctx.stroke();
- }
- }
- // 使用示例
- drawRectangleWithPath(200, 200, 200, 100, '#f39c12', '#d68910', 3);
复制代码
2.6 清除矩形区域
使用clearRect()方法可以清除指定矩形区域的内容:
- function clearRectangleArea(x, y, width, height) {
- ctx.clearRect(x, y, width, height);
- }
- // 使用示例
- // 在画布上绘制一个矩形
- drawFilledRectangle(50, 50, 200, 100, '#3498db');
- // 清除矩形的一部分
- clearRectangleArea(100, 75, 100, 50);
复制代码
3. SVG绘制矩形
SVG(Scalable Vector Graphics)是一种基于XML的矢量图像格式,它也可以用来绘制矩形。与Canvas不同,SVG创建的图形是DOM元素,可以直接通过CSS样式化或通过JavaScript操作。
3.1 基本SVG矩形
在HTML中创建SVG矩形:
- <svg width="500" height="300">
- <rect x="50" y="50" width="200" height="100" fill="#3498db" />
- </svg>
复制代码
3.2 使用JavaScript动态创建SVG矩形
我们可以使用JavaScript动态创建SVG矩形元素:
- function createSVGRectangle(container, x, y, width, height, fill, stroke, strokeWidth) {
- // 创建SVG元素
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('width', '500');
- svg.setAttribute('height', '300');
-
- // 创建矩形元素
- const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
- rect.setAttribute('x', x);
- rect.setAttribute('y', y);
- rect.setAttribute('width', width);
- rect.setAttribute('height', height);
-
- if (fill) rect.setAttribute('fill', fill);
- if (stroke) rect.setAttribute('stroke', stroke);
- if (strokeWidth) rect.setAttribute('stroke-width', strokeWidth);
-
- // 将矩形添加到SVG中
- svg.appendChild(rect);
-
- // 将SVG添加到容器中
- container.appendChild(svg);
-
- return rect;
- }
- // 使用示例
- const container = document.getElementById('svg-container');
- createSVGRectangle(container, 50, 50, 200, 100, '#e74c3c', '#c0392b', 3);
复制代码
3.3 SVG矩形的圆角
SVG矩形支持圆角,通过rx和ry属性设置:
- function createRoundedSVGRectangle(container, x, y, width, height, rx, ry, fill, stroke, strokeWidth) {
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('width', '500');
- svg.setAttribute('height', '300');
-
- const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
- rect.setAttribute('x', x);
- rect.setAttribute('y', y);
- rect.setAttribute('width', width);
- rect.setAttribute('height', height);
- rect.setAttribute('rx', rx);
- rect.setAttribute('ry', ry);
-
- if (fill) rect.setAttribute('fill', fill);
- if (stroke) rect.setAttribute('stroke', stroke);
- if (strokeWidth) rect.setAttribute('stroke-width', strokeWidth);
-
- svg.appendChild(rect);
- container.appendChild(svg);
-
- return rect;
- }
- // 使用示例
- createRoundedSVGRectangle(container, 100, 100, 200, 100, 10, 10, '#2ecc71', '#27ae60', 3);
复制代码
4. 使用JavaScript库绘制矩形
除了原生API,我们还可以使用各种JavaScript库来简化矩形绘制过程。
4.1 使用jQuery创建矩形
jQuery可以简化DOM操作,使创建和操作矩形更加容易:
- <div id="rectangle-container"></div>
复制代码- function createJQueryRectangle(container, width, height, color) {
- const $rectangle = $('<div></div>')
- .css({
- 'width': width + 'px',
- 'height': height + 'px',
- 'background-color': color
- })
- .appendTo(container);
-
- return $rectangle;
- }
- // 使用示例
- const $container = $('#rectangle-container');
- const $myRectangle = createJQueryRectangle($container, 300, 150, '#9b59b6');
复制代码
4.2 使用D3.js创建矩形
D3.js是一个强大的数据可视化库,它提供了创建和操作SVG元素的方法:
- <div id="d3-container"></div>
复制代码- function createD3Rectangle(container, x, y, width, height, fill, stroke, strokeWidth) {
- // 创建SVG容器
- const svg = d3.select(container)
- .append('svg')
- .attr('width', 500)
- .attr('height', 300);
-
- // 创建矩形
- const rectangle = svg.append('rect')
- .attr('x', x)
- .attr('y', y)
- .attr('width', width)
- .attr('height', height)
- .attr('fill', fill);
-
- if (stroke) {
- rectangle.attr('stroke', stroke);
- }
-
- if (strokeWidth) {
- rectangle.attr('stroke-width', strokeWidth);
- }
-
- return rectangle;
- }
- // 使用示例
- const d3Container = document.getElementById('d3-container');
- createD3Rectangle(d3Container, 50, 50, 200, 100, '#1abc9c', '#16a085', 3);
复制代码
4.3 使用Paper.js创建矩形
Paper.js是一个强大的矢量图形脚本框架,它运行在Canvas之上:
- <canvas id="paper-canvas" width="500" height="300"></canvas>
复制代码- // 初始化Paper.js
- paper.setup('paper-canvas');
- function createPaperRectangle(x, y, width, height, fillColor, strokeColor, strokeWidth) {
- const rectangle = new paper.Rectangle(new paper.Point(x, y), new paper.Size(width, height));
- const path = new paper.Path.Rectangle(rectangle);
-
- if (fillColor) {
- path.fillColor = fillColor;
- }
-
- if (strokeColor) {
- path.strokeColor = strokeColor;
- }
-
- if (strokeWidth) {
- path.strokeWidth = strokeWidth;
- }
-
- paper.view.draw();
-
- return path;
- }
- // 使用示例
- createPaperRectangle(100, 100, 200, 100, '#f1c40f', '#f39c12', 3);
复制代码
5. 高级技巧:动画矩形和交互式矩形
5.1 使用CSS动画创建动画矩形
我们可以使用CSS动画为矩形添加简单的动画效果:
- <div class="animated-rectangle"></div>
复制代码- .animated-rectangle {
- width: 100px;
- height: 100px;
- background-color: #3498db;
- animation: moveAndChange 3s infinite alternate;
- }
- @keyframes moveAndChange {
- 0% {
- transform: translateX(0);
- background-color: #3498db;
- }
- 100% {
- transform: translateX(400px);
- background-color: #e74c3c;
- }
- }
复制代码
5.2 使用JavaScript控制矩形动画
使用JavaScript可以更灵活地控制矩形动画:
- <canvas id="animation-canvas" width="500" height="300"></canvas>
复制代码- const canvas = document.getElementById('animation-canvas');
- const ctx = canvas.getContext('2d');
- let x = 50;
- let y = 50;
- let width = 100;
- let height = 100;
- let dx = 2; // x方向速度
- let dy = 1; // y方向速度
- let color = '#3498db';
- function animateRectangle() {
- // 清除画布
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制矩形
- ctx.fillStyle = color;
- ctx.fillRect(x, y, width, height);
-
- // 更新位置
- x += dx;
- y += dy;
-
- // 边界检测
- if (x + width > canvas.width || x < 0) {
- dx = -dx;
- color = getRandomColor();
- }
-
- if (y + height > canvas.height || y < 0) {
- dy = -dy;
- color = getRandomColor();
- }
-
- // 继续动画
- requestAnimationFrame(animateRectangle);
- }
- function getRandomColor() {
- const letters = '0123456789ABCDEF';
- let color = '#';
- for (let i = 0; i < 6; i++) {
- color += letters[Math.floor(Math.random() * 16)];
- }
- return color;
- }
- // 开始动画
- animateRectangle();
复制代码
5.3 创建交互式矩形
我们可以创建响应用户交互的矩形:
- <canvas id="interactive-canvas" width="500" height="300"></canvas>
复制代码- const canvas = document.getElementById('interactive-canvas');
- const ctx = canvas.getContext('2d');
- // 矩形对象
- const rectangle = {
- x: 200,
- y: 150,
- width: 100,
- height: 100,
- color: '#3498db',
- isDragging: false,
- offsetX: 0,
- offsetY: 0
- };
- // 绘制矩形
- function drawRectangle() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.fillStyle = rectangle.color;
- ctx.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);
- }
- // 检查点是否在矩形内
- function isPointInRectangle(x, y) {
- return x >= rectangle.x &&
- x <= rectangle.x + rectangle.width &&
- y >= rectangle.y &&
- y <= rectangle.y + rectangle.height;
- }
- // 鼠标按下事件
- canvas.addEventListener('mousedown', (e) => {
- const rect = canvas.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
-
- if (isPointInRectangle(x, y)) {
- rectangle.isDragging = true;
- rectangle.offsetX = x - rectangle.x;
- rectangle.offsetY = y - rectangle.y;
- canvas.style.cursor = 'grabbing';
- }
- });
- // 鼠标移动事件
- canvas.addEventListener('mousemove', (e) => {
- const rect = canvas.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
-
- if (rectangle.isDragging) {
- rectangle.x = x - rectangle.offsetX;
- rectangle.y = y - rectangle.offsetY;
- drawRectangle();
- } else if (isPointInRectangle(x, y)) {
- canvas.style.cursor = 'grab';
- } else {
- canvas.style.cursor = 'default';
- }
- });
- // 鼠标释放事件
- canvas.addEventListener('mouseup', () => {
- rectangle.isDragging = false;
- canvas.style.cursor = 'default';
- });
- // 初始绘制
- drawRectangle();
复制代码
5.4 使用WebGL创建3D矩形
WebGL允许我们在浏览器中创建3D图形。下面是一个使用WebGL创建3D旋转矩形的示例:
- <canvas id="webgl-canvas" width="500" height="300"></canvas>
复制代码- const canvas = document.getElementById('webgl-canvas');
- const gl = canvas.getContext('webgl');
- // 顶点着色器源码
- const vsSource = `
- attribute vec4 aVertexPosition;
- attribute vec4 aVertexColor;
-
- uniform mat4 uModelViewMatrix;
- uniform mat4 uProjectionMatrix;
-
- varying lowp vec4 vColor;
-
- void main(void) {
- gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition;
- vColor = aVertexColor;
- }
- `;
- // 片段着色器源码
- const fsSource = `
- varying lowp vec4 vColor;
-
- void main(void) {
- gl_FragColor = vColor;
- }
- `;
- // 创建着色器
- function loadShader(gl, type, source) {
- const shader = gl.createShader(type);
- gl.shaderSource(shader, source);
- gl.compileShader(shader);
-
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
- console.error('An error occurred compiling the shaders: ' + gl.getShaderInfoLog(shader));
- gl.deleteShader(shader);
- return null;
- }
-
- return shader;
- }
- // 初始化着色器程序
- function initShaderProgram(gl, vsSource, fsSource) {
- const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource);
- const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);
-
- const shaderProgram = gl.createProgram();
- gl.attachShader(shaderProgram, vertexShader);
- gl.attachShader(shaderProgram, fragmentShader);
- gl.linkProgram(shaderProgram);
-
- if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
- console.error('Unable to initialize the shader program: ' + gl.getProgramInfoLog(shaderProgram));
- return null;
- }
-
- return shaderProgram;
- }
- const shaderProgram = initShaderProgram(gl, vsSource, fsSource);
- const programInfo = {
- program: shaderProgram,
- attribLocations: {
- vertexPosition: gl.getAttribLocation(shaderProgram, 'aVertexPosition'),
- vertexColor: gl.getAttribLocation(shaderProgram, 'aVertexColor'),
- },
- uniformLocations: {
- projectionMatrix: gl.getUniformLocation(shaderProgram, 'uProjectionMatrix'),
- modelViewMatrix: gl.getUniformLocation(shaderProgram, 'uModelViewMatrix'),
- },
- };
- // 创建矩形缓冲区
- function initBuffers(gl) {
- // 矩形顶点位置
- const positions = [
- // 前面
- -1.0, -1.0, 1.0,
- 1.0, -1.0, 1.0,
- 1.0, 1.0, 1.0,
- -1.0, 1.0, 1.0,
-
- // 后面
- -1.0, -1.0, -1.0,
- -1.0, 1.0, -1.0,
- 1.0, 1.0, -1.0,
- 1.0, -1.0, -1.0,
-
- // 顶面
- -1.0, 1.0, -1.0,
- -1.0, 1.0, 1.0,
- 1.0, 1.0, 1.0,
- 1.0, 1.0, -1.0,
-
- // 底面
- -1.0, -1.0, -1.0,
- 1.0, -1.0, -1.0,
- 1.0, -1.0, 1.0,
- -1.0, -1.0, 1.0,
-
- // 右面
- 1.0, -1.0, -1.0,
- 1.0, 1.0, -1.0,
- 1.0, 1.0, 1.0,
- 1.0, -1.0, 1.0,
-
- // 左面
- -1.0, -1.0, -1.0,
- -1.0, -1.0, 1.0,
- -1.0, 1.0, 1.0,
- -1.0, 1.0, -1.0,
- ];
-
- const positionBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
-
- // 矩形面颜色
- const faceColors = [
- [1.0, 0.0, 0.0, 1.0], // 前面: 红色
- [0.0, 1.0, 0.0, 1.0], // 后面: 绿色
- [0.0, 0.0, 1.0, 1.0], // 顶面: 蓝色
- [1.0, 1.0, 0.0, 1.0], // 底面: 黄色
- [1.0, 0.0, 1.0, 1.0], // 右面: 紫色
- [0.0, 1.0, 1.0, 1.0], // 左面: 青色
- ];
-
- let colors = [];
- for (let j = 0; j < faceColors.length; ++j) {
- const c = faceColors[j];
- colors = colors.concat(c, c, c, c);
- }
-
- const colorBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
- gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);
-
- // 矩形索引
- const indices = [
- 0, 1, 2, 0, 2, 3, // 前面
- 4, 5, 6, 4, 6, 7, // 后面
- 8, 9, 10, 8, 10, 11, // 顶面
- 12, 13, 14, 12, 14, 15, // 底面
- 16, 17, 18, 16, 18, 19, // 右面
- 20, 21, 22, 20, 22, 23, // 左面
- ];
-
- const indexBuffer = gl.createBuffer();
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
-
- return {
- position: positionBuffer,
- color: colorBuffer,
- indices: indexBuffer,
- };
- }
- const buffers = initBuffers(gl);
- // 绘制场景
- function drawScene(gl, programInfo, buffers, deltaTime) {
- gl.clearColor(0.0, 0.0, 0.0, 1.0);
- gl.clearDepth(1.0);
- gl.enable(gl.DEPTH_TEST);
- gl.depthFunc(gl.LEQUAL);
-
- gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
-
- // 创建透视矩阵
- const fieldOfView = 45 * Math.PI / 180;
- const aspect = gl.canvas.clientWidth / gl.canvas.clientHeight;
- const zNear = 0.1;
- const zFar = 100.0;
- const projectionMatrix = mat4.create();
-
- mat4.perspective(projectionMatrix, fieldOfView, aspect, zNear, zFar);
-
- // 创建模型视图矩阵
- const modelViewMatrix = mat4.create();
-
- mat4.translate(modelViewMatrix, modelViewMatrix, [-0.0, 0.0, -6.0]);
- mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation, [0, 0, 1]);
- mat4.rotate(modelViewMatrix, modelViewMatrix, cubeRotation * .7, [0, 1, 0]);
-
- // 设置位置属性
- {
- const numComponents = 3;
- const type = gl.FLOAT;
- const normalize = false;
- const stride = 0;
- const offset = 0;
- gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position);
- gl.vertexAttribPointer(
- programInfo.attribLocations.vertexPosition,
- numComponents,
- type,
- normalize,
- stride,
- offset);
- gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition);
- }
-
- // 设置颜色属性
- {
- const numComponents = 4;
- const type = gl.FLOAT;
- const normalize = false;
- const stride = 0;
- const offset = 0;
- gl.bindBuffer(gl.ARRAY_BUFFER, buffers.color);
- gl.vertexAttribPointer(
- programInfo.attribLocations.vertexColor,
- numComponents,
- type,
- normalize,
- stride,
- offset);
- gl.enableVertexAttribArray(programInfo.attribLocations.vertexColor);
- }
-
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);
-
- gl.useProgram(programInfo.program);
-
- gl.uniformMatrix4fv(
- programInfo.uniformLocations.projectionMatrix,
- false,
- projectionMatrix);
- gl.uniformMatrix4fv(
- programInfo.uniformLocations.modelViewMatrix,
- false,
- modelViewMatrix);
-
- {
- const vertexCount = 36;
- const type = gl.UNSIGNED_SHORT;
- const offset = 0;
- gl.drawElements(gl.TRIANGLES, vertexCount, type, offset);
- }
-
- cubeRotation += deltaTime;
- }
- let cubeRotation = 0.0;
- let then = 0;
- // 渲染循环
- function render(now) {
- now *= 0.001;
- const deltaTime = now - then;
- then = now;
-
- drawScene(gl, programInfo, buffers, deltaTime);
-
- requestAnimationFrame(render);
- }
- requestAnimationFrame(render);
复制代码
注意:上面的WebGL示例使用了gl-matrix库来进行矩阵运算。在实际使用中,你需要先引入这个库。
6. 性能优化和最佳实践
6.1 Canvas性能优化
当使用Canvas绘制大量矩形时,性能可能会成为一个问题。以下是一些优化技巧:
只重绘发生变化的部分,而不是整个画布:
- // 不好的做法:每次清除整个画布
- function animate() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- drawAllRectangles();
- requestAnimationFrame(animate);
- }
- // 好的做法:只清除需要重绘的部分
- function animate() {
- // 只清除上一帧的矩形位置
- ctx.clearRect(prevX, prevY, width, height);
-
- // 绘制新位置的矩形
- drawRectangle(x, y, width, height);
-
- // 更新上一帧的位置
- prevX = x;
- prevY = y;
-
- requestAnimationFrame(animate);
- }
复制代码
对于复杂的静态内容,可以使用离屏Canvas进行预渲染:
- // 创建离屏Canvas
- const offscreenCanvas = document.createElement('canvas');
- const offscreenCtx = offscreenCanvas.getContext('2d');
- offscreenCanvas.width = 500;
- offscreenCanvas.height = 300;
- // 在离屏Canvas上绘制静态内容
- function drawStaticContent() {
- offscreenCtx.fillStyle = '#ecf0f1';
- offscreenCtx.fillRect(0, 0, offscreenCanvas.width, offscreenCanvas.height);
-
- // 绘制多个静态矩形
- for (let i = 0; i < 50; i++) {
- const x = Math.random() * offscreenCanvas.width;
- const y = Math.random() * offscreenCanvas.height;
- const width = 20 + Math.random() * 30;
- const height = 20 + Math.random() * 30;
- const color = `hsl(${Math.random() * 360}, 70%, 60%)`;
-
- offscreenCtx.fillStyle = color;
- offscreenCtx.fillRect(x, y, width, height);
- }
- }
- // 在主Canvas上绘制离屏Canvas的内容
- function drawScene() {
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制离屏Canvas的内容
- ctx.drawImage(offscreenCanvas, 0, 0);
-
- // 绘制动态内容
- drawDynamicRectangle();
- }
- // 初始化
- drawStaticContent();
复制代码
尽量减少绘制调用的次数,批量处理相似的绘制操作:
- // 不好的做法:多次调用绘制方法
- function drawMultipleRectanglesBad(rectangles) {
- rectangles.forEach(rect => {
- ctx.fillStyle = rect.color;
- ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
- });
- }
- // 好的做法:批量处理相同颜色的矩形
- function drawMultipleRectanglesGood(rectangles) {
- // 按颜色分组
- const rectanglesByColor = {};
- rectangles.forEach(rect => {
- if (!rectanglesByColor[rect.color]) {
- rectanglesByColor[rect.color] = [];
- }
- rectanglesByColor[rect.color].push(rect);
- });
-
- // 批量绘制
- Object.keys(rectanglesByColor).forEach(color => {
- ctx.fillStyle = color;
- rectanglesByColor[color].forEach(rect => {
- ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
- });
- });
- }
复制代码
6.2 SVG性能优化
当需要显示大量矩形时,尽量减少SVG元素的数量:
- // 不好的做法:为每个矩形创建一个SVG元素
- function createManyRectanglesBad(count) {
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('width', '500');
- svg.setAttribute('height', '300');
-
- for (let i = 0; i < count; i++) {
- const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
- rect.setAttribute('x', Math.random() * 450);
- rect.setAttribute('y', Math.random() * 250);
- rect.setAttribute('width', 20 + Math.random() * 30);
- rect.setAttribute('height', 20 + Math.random() * 30);
- rect.setAttribute('fill', `hsl(${Math.random() * 360}, 70%, 60%)`);
- svg.appendChild(rect);
- }
-
- return svg;
- }
- // 好的做法:使用单个path元素绘制多个矩形
- function createManyRectanglesGood(count) {
- const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
- svg.setAttribute('width', '500');
- svg.setAttribute('height', '300');
-
- const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
- let pathData = '';
-
- for (let i = 0; i < count; i++) {
- const x = Math.random() * 450;
- const y = Math.random() * 250;
- const width = 20 + Math.random() * 30;
- const height = 20 + Math.random() * 30;
-
- pathData += `M ${x} ${y} L ${x + width} ${y} L ${x + width} ${y + height} L ${x} ${y + height} Z `;
- }
-
- path.setAttribute('d', pathData);
- path.setAttribute('fill', 'none');
- path.setAttribute('stroke', '#333');
- path.setAttribute('stroke-width', '1');
-
- svg.appendChild(path);
- return svg;
- }
复制代码
对于简单的动画,使用CSS动画比JavaScript动画性能更好:
- <svg width="500" height="300">
- <rect class="animated-rect" x="50" y="50" width="100" height="100" fill="#3498db" />
- </svg>
复制代码- .animated-rect {
- animation: move 3s infinite alternate;
- transform-origin: center;
- }
- @keyframes move {
- 0% {
- transform: translateX(0) rotate(0deg);
- }
- 100% {
- transform: translateX(300px) rotate(180deg);
- }
- }
复制代码
6.3 通用最佳实践
对于动画,使用requestAnimationFrame而不是setTimeout或setInterval:
- // 不好的做法
- function animateBad() {
- updateRectanglePosition();
- drawRectangle();
- setTimeout(animateBad, 16); // 约60fps
- }
- // 好的做法
- function animateGood() {
- updateRectanglePosition();
- drawRectangle();
- requestAnimationFrame(animateGood);
- }
- // 开始动画
- animateGood();
复制代码
在动画循环中,避免重复计算不变的值:
- // 不好的做法:每次循环都计算
- function animateBad() {
- const centerX = canvas.width / 2;
- const centerY = canvas.height / 2;
- const radius = 100;
-
- const angle = Date.now() * 0.001;
- const x = centerX + Math.cos(angle) * radius;
- const y = centerY + Math.sin(angle) * radius;
-
- drawRectangle(x, y, 50, 50);
- requestAnimationFrame(animateBad);
- }
- // 好的做法:预先计算不变的值
- function animateGood() {
- // 这些值在循环外计算一次
- const centerX = canvas.width / 2;
- const centerY = canvas.height / 2;
- const radius = 100;
-
- return function animate() {
- const angle = Date.now() * 0.001;
- const x = centerX + Math.cos(angle) * radius;
- const y = centerY + Math.sin(angle) * radius;
-
- drawRectangle(x, y, 50, 50);
- requestAnimationFrame(animate);
- };
- }
- // 开始动画
- const animate = animateGood();
- animate();
复制代码
对于频繁创建和销毁的对象,使用对象池模式可以提高性能:
- // 矩形对象池
- const rectanglePool = {
- objects: [],
- new: function(x, y, width, height, color) {
- if (this.objects.length > 0) {
- const rect = this.objects.pop();
- rect.x = x;
- rect.y = y;
- rect.width = width;
- rect.height = height;
- rect.color = color;
- return rect;
- } else {
- return { x, y, width, height, color };
- }
- },
- free: function(rect) {
- this.objects.push(rect);
- }
- };
- // 使用对象池
- function drawManyRectangles(count) {
- const rectangles = [];
-
- // 创建矩形
- for (let i = 0; i < count; i++) {
- const rect = rectanglePool.new(
- Math.random() * 450,
- Math.random() * 250,
- 20 + Math.random() * 30,
- 20 + Math.random() * 30,
- `hsl(${Math.random() * 360}, 70%, 60%)`
- );
- rectangles.push(rect);
- }
-
- // 绘制矩形
- rectangles.forEach(rect => {
- ctx.fillStyle = rect.color;
- ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
- });
-
- // 释放矩形回对象池
- rectangles.forEach(rect => {
- rectanglePool.free(rect);
- });
- }
复制代码
7. 实际应用案例
7.1 数据可视化:柱状图
矩形在数据可视化中非常常见,例如柱状图:
- <canvas id="chart-canvas" width="600" height="400"></canvas>
复制代码- const canvas = document.getElementById('chart-canvas');
- const ctx = canvas.getContext('2d');
- // 数据
- const data = [
- { label: 'A', value: 30 },
- { label: 'B', value: 50 },
- { label: 'C', value: 80 },
- { label: 'D', value: 40 },
- { label: 'E', value: 70 },
- { label: 'F', value: 20 }
- ];
- // 图表配置
- const chartConfig = {
- padding: 40,
- barWidth: 60,
- barSpacing: 20,
- colors: ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6', '#1abc9c']
- };
- // 计算最大值
- const maxValue = Math.max(...data.map(item => item.value));
- // 绘制坐标轴
- function drawAxes() {
- ctx.beginPath();
- ctx.strokeStyle = '#333';
- ctx.lineWidth = 2;
-
- // Y轴
- ctx.moveTo(chartConfig.padding, chartConfig.padding);
- ctx.lineTo(chartConfig.padding, canvas.height - chartConfig.padding);
-
- // X轴
- ctx.moveTo(chartConfig.padding, canvas.height - chartConfig.padding);
- ctx.lineTo(canvas.width - chartConfig.padding, canvas.height - chartConfig.padding);
-
- ctx.stroke();
-
- // 绘制Y轴刻度
- const yAxisSteps = 5;
- for (let i = 0; i <= yAxisSteps; i++) {
- const y = canvas.height - chartConfig.padding - (i / yAxisSteps) * (canvas.height - 2 * chartConfig.padding);
- const value = Math.round((i / yAxisSteps) * maxValue);
-
- ctx.beginPath();
- ctx.moveTo(chartConfig.padding - 5, y);
- ctx.lineTo(chartConfig.padding, y);
- ctx.stroke();
-
- ctx.fillStyle = '#333';
- ctx.font = '12px Arial';
- ctx.textAlign = 'right';
- ctx.textBaseline = 'middle';
- ctx.fillText(value, chartConfig.padding - 10, y);
- }
- }
- // 绘制柱状图
- function drawBarChart() {
- // 清除画布
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制坐标轴
- drawAxes();
-
- // 计算图表高度
- const chartHeight = canvas.height - 2 * chartConfig.padding;
-
- // 绘制柱子
- data.forEach((item, index) => {
- const x = chartConfig.padding + index * (chartConfig.barWidth + chartConfig.barSpacing) + chartConfig.barSpacing;
- const barHeight = (item.value / maxValue) * chartHeight;
- const y = canvas.height - chartConfig.padding - barHeight;
-
- // 绘制柱子
- ctx.fillStyle = chartConfig.colors[index % chartConfig.colors.length];
- ctx.fillRect(x, y, chartConfig.barWidth, barHeight);
-
- // 绘制标签
- ctx.fillStyle = '#333';
- ctx.font = '14px Arial';
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- ctx.fillText(item.label, x + chartConfig.barWidth / 2, canvas.height - chartConfig.padding + 10);
-
- // 绘制数值
- ctx.textBaseline = 'bottom';
- ctx.fillText(item.value, x + chartConfig.barWidth / 2, y);
- });
- }
- // 初始绘制
- drawBarChart();
- // 添加动画效果
- let animationProgress = 0;
- const animationDuration = 1000; // 1秒
- const startTime = Date.now();
- function animateChart() {
- const currentTime = Date.now();
- animationProgress = Math.min((currentTime - startTime) / animationDuration, 1);
-
- // 清除画布
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制坐标轴
- drawAxes();
-
- // 计算图表高度
- const chartHeight = canvas.height - 2 * chartConfig.padding;
-
- // 绘制柱子(带动画)
- data.forEach((item, index) => {
- const x = chartConfig.padding + index * (chartConfig.barWidth + chartConfig.barSpacing) + chartConfig.barSpacing;
- const targetHeight = (item.value / maxValue) * chartHeight;
- const barHeight = targetHeight * animationProgress;
- const y = canvas.height - chartConfig.padding - barHeight;
-
- // 绘制柱子
- ctx.fillStyle = chartConfig.colors[index % chartConfig.colors.length];
- ctx.fillRect(x, y, chartConfig.barWidth, barHeight);
-
- // 绘制标签
- ctx.fillStyle = '#333';
- ctx.font = '14px Arial';
- ctx.textAlign = 'center';
- ctx.textBaseline = 'top';
- ctx.fillText(item.label, x + chartConfig.barWidth / 2, canvas.height - chartConfig.padding + 10);
-
- // 绘制数值(只在动画完成后显示)
- if (animationProgress === 1) {
- ctx.textBaseline = 'bottom';
- ctx.fillText(item.value, x + chartConfig.barWidth / 2, y);
- }
- });
-
- // 继续动画
- if (animationProgress < 1) {
- requestAnimationFrame(animateChart);
- }
- }
- // 开始动画
- animateChart();
复制代码
7.2 游戏开发:简单的平台游戏
矩形在游戏开发中非常常见,例如平台游戏中的角色、平台和障碍物:
- <canvas id="game-canvas" width="800" height="400"></canvas>
复制代码- const canvas = document.getElementById('game-canvas');
- const ctx = canvas.getContext('2d');
- // 游戏状态
- const game = {
- gravity: 0.5,
- friction: 0.8,
- isRunning: true
- };
- // 玩家对象
- const player = {
- x: 100,
- y: 200,
- width: 30,
- height: 50,
- velocityX: 0,
- velocityY: 0,
- speed: 5,
- jumpPower: 12,
- color: '#3498db',
- isJumping: false
- };
- // 平台数组
- const platforms = [
- { x: 0, y: 350, width: 800, height: 50, color: '#2c3e50' },
- { x: 200, y: 280, width: 100, height: 20, color: '#2c3e50' },
- { x: 400, y: 220, width: 100, height: 20, color: '#2c3e50' },
- { x: 600, y: 160, width: 100, height: 20, color: '#2c3e50' }
- ];
- // 障碍物数组
- const obstacles = [
- { x: 300, y: 320, width: 30, height: 30, color: '#e74c3c' },
- { x: 500, y: 190, width: 30, height: 30, color: '#e74c3c' }
- ];
- // 收集品数组
- const collectibles = [
- { x: 250, y: 250, width: 20, height: 20, color: '#f1c40f', collected: false },
- { x: 450, y: 190, width: 20, height: 20, color: '#f1c40f', collected: false },
- { x: 650, y: 130, width: 20, height: 20, color: '#f1c40f', collected: false }
- ];
- // 键盘输入
- const keys = {};
- document.addEventListener('keydown', (e) => {
- keys[e.key] = true;
- });
- document.addEventListener('keyup', (e) => {
- keys[e.key] = false;
- });
- // 更新玩家状态
- function updatePlayer() {
- // 左右移动
- if (keys['ArrowLeft'] || keys['a']) {
- player.velocityX = -player.speed;
- } else if (keys['ArrowRight'] || keys['d']) {
- player.velocityX = player.speed;
- } else {
- player.velocityX *= game.friction;
- }
-
- // 跳跃
- if ((keys['ArrowUp'] || keys['w'] || keys[' ']) && !player.isJumping) {
- player.velocityY = -player.jumpPower;
- player.isJumping = true;
- }
-
- // 应用重力
- player.velocityY += game.gravity;
-
- // 更新位置
- player.x += player.velocityX;
- player.y += player.velocityY;
-
- // 边界检查
- if (player.x < 0) {
- player.x = 0;
- player.velocityX = 0;
- } else if (player.x + player.width > canvas.width) {
- player.x = canvas.width - player.width;
- player.velocityX = 0;
- }
-
- // 检查是否掉出屏幕
- if (player.y > canvas.height) {
- resetPlayer();
- }
- }
- // 重置玩家位置
- function resetPlayer() {
- player.x = 100;
- player.y = 200;
- player.velocityX = 0;
- player.velocityY = 0;
- }
- // 碰撞检测
- function checkCollisions() {
- // 平台碰撞
- player.isJumping = true;
- platforms.forEach(platform => {
- if (
- player.x < platform.x + platform.width &&
- player.x + player.width > platform.x &&
- player.y < platform.y + platform.height &&
- player.y + player.height > platform.y
- ) {
- // 从上方碰撞
- if (player.velocityY > 0 && player.y < platform.y) {
- player.y = platform.y - player.height;
- player.velocityY = 0;
- player.isJumping = false;
- }
- // 从下方碰撞
- else if (player.velocityY < 0 && player.y > platform.y) {
- player.y = platform.y + platform.height;
- player.velocityY = 0;
- }
- // 从左侧碰撞
- else if (player.velocityX > 0 && player.x < platform.x) {
- player.x = platform.x - player.width;
- player.velocityX = 0;
- }
- // 从右侧碰撞
- else if (player.velocityX < 0 && player.x > platform.x) {
- player.x = platform.x + platform.width;
- player.velocityX = 0;
- }
- }
- });
-
- // 障碍物碰撞
- obstacles.forEach(obstacle => {
- if (
- player.x < obstacle.x + obstacle.width &&
- player.x + player.width > obstacle.x &&
- player.y < obstacle.y + obstacle.height &&
- player.y + player.height > obstacle.y
- ) {
- resetPlayer();
- }
- });
-
- // 收集品碰撞
- collectibles.forEach(collectible => {
- if (
- !collectible.collected &&
- player.x < collectible.x + collectible.width &&
- player.x + player.width > collectible.x &&
- player.y < collectible.y + collectible.height &&
- player.y + player.height > collectible.y
- ) {
- collectible.collected = true;
- }
- });
- }
- // 绘制游戏
- function drawGame() {
- // 清除画布
- ctx.clearRect(0, 0, canvas.width, canvas.height);
-
- // 绘制背景
- ctx.fillStyle = '#ecf0f1';
- ctx.fillRect(0, 0, canvas.width, canvas.height);
-
- // 绘制平台
- platforms.forEach(platform => {
- ctx.fillStyle = platform.color;
- ctx.fillRect(platform.x, platform.y, platform.width, platform.height);
- });
-
- // 绘制障碍物
- obstacles.forEach(obstacle => {
- ctx.fillStyle = obstacle.color;
- ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
- });
-
- // 绘制收集品
- collectibles.forEach(collectible => {
- if (!collectible.collected) {
- ctx.fillStyle = collectible.color;
- ctx.fillRect(collectible.x, collectible.y, collectible.width, collectible.height);
- }
- });
-
- // 绘制玩家
- ctx.fillStyle = player.color;
- ctx.fillRect(player.x, player.y, player.width, player.height);
-
- // 绘制分数
- const collectedCount = collectibles.filter(c => c.collected).length;
- ctx.fillStyle = '#333';
- ctx.font = '20px Arial';
- ctx.textAlign = 'left';
- ctx.fillText(`收集品: ${collectedCount}/${collectibles.length}`, 20, 30);
-
- // 检查胜利条件
- if (collectedCount === collectibles.length) {
- ctx.fillStyle = '#2ecc71';
- ctx.font = '40px Arial';
- ctx.textAlign = 'center';
- ctx.fillText('你赢了!', canvas.width / 2, canvas.height / 2);
- game.isRunning = false;
- }
- }
- // 游戏循环
- function gameLoop() {
- if (game.isRunning) {
- updatePlayer();
- checkCollisions();
- }
-
- drawGame();
- requestAnimationFrame(gameLoop);
- }
- // 开始游戏
- gameLoop();
复制代码
7.3 图像处理:使用矩形选择区域
矩形在图像处理中也很有用,例如选择图像的特定区域:
- <div>
- <canvas id="image-canvas" width="600" height="400"></canvas>
- <canvas id="selection-canvas" width="200" height="200"></canvas>
- </div>
复制代码- const imageCanvas = document.getElementById('image-canvas');
- const imageCtx = imageCanvas.getContext('2d');
- const selectionCanvas = document.getElementById('selection-canvas');
- const selectionCtx = selectionCanvas.getContext('2d');
- // 加载图像
- const img = new Image();
- img.crossOrigin = 'Anonymous';
- img.onload = function() {
- // 绘制图像到画布
- imageCtx.drawImage(img, 0, 0, imageCanvas.width, imageCanvas.height);
- };
- img.src = 'https://picsum.photos/seed/rectangle-demo/600/400.jpg';
- // 选择区域
- const selection = {
- x: 100,
- y: 100,
- width: 200,
- height: 150,
- isDragging: false,
- isResizing: false,
- dragStartX: 0,
- dragStartY: 0,
- resizeHandle: null
- };
- // 绘制选择矩形
- function drawSelection() {
- // 清除画布并重新绘制图像
- imageCtx.clearRect(0, 0, imageCanvas.width, imageCanvas.height);
- imageCtx.drawImage(img, 0, 0, imageCanvas.width, imageCanvas.height);
-
- // 绘制半透明覆盖层
- imageCtx.fillStyle = 'rgba(0, 0, 0, 0.5)';
- imageCtx.fillRect(0, 0, imageCanvas.width, imageCanvas.height);
-
- // 清除选择区域
- imageCtx.clearRect(selection.x, selection.y, selection.width, selection.height);
- imageCtx.drawImage(
- img,
- selection.x * (img.width / imageCanvas.width),
- selection.y * (img.height / imageCanvas.height),
- selection.width * (img.width / imageCanvas.width),
- selection.height * (img.height / imageCanvas.height),
- selection.x,
- selection.y,
- selection.width,
- selection.height
- );
-
- // 绘制选择边框
- imageCtx.strokeStyle = '#3498db';
- imageCtx.lineWidth = 2;
- imageCtx.strokeRect(selection.x, selection.y, selection.width, selection.height);
-
- // 绘制调整大小的手柄
- const handleSize = 8;
- imageCtx.fillStyle = '#3498db';
-
- // 四个角的手柄
- imageCtx.fillRect(selection.x - handleSize/2, selection.y - handleSize/2, handleSize, handleSize); // 左上
- imageCtx.fillRect(selection.x + selection.width - handleSize/2, selection.y - handleSize/2, handleSize, handleSize); // 右上
- imageCtx.fillRect(selection.x - handleSize/2, selection.y + selection.height - handleSize/2, handleSize, handleSize); // 左下
- imageCtx.fillRect(selection.x + selection.width - handleSize/2, selection.y + selection.height - handleSize/2, handleSize, handleSize); // 右下
-
- // 更新选择区域的图像
- updateSelectionImage();
- }
- // 更新选择区域的图像
- function updateSelectionImage() {
- selectionCtx.clearRect(0, 0, selectionCanvas.width, selectionCanvas.height);
- selectionCtx.drawImage(
- imageCanvas,
- selection.x, selection.y, selection.width, selection.height,
- 0, 0, selectionCanvas.width, selectionCanvas.height
- );
- }
- // 检查点是否在选择矩形内
- function isPointInSelection(x, y) {
- return x >= selection.x &&
- x <= selection.x + selection.width &&
- y >= selection.y &&
- y <= selection.y + selection.height;
- }
- // 检查点是否在调整大小的手柄上
- function getResizeHandle(x, y) {
- const handleSize = 8;
- const tolerance = handleSize;
-
- // 左上角
- if (Math.abs(x - selection.x) < tolerance && Math.abs(y - selection.y) < tolerance) {
- return 'nw';
- }
- // 右上角
- if (Math.abs(x - (selection.x + selection.width)) < tolerance && Math.abs(y - selection.y) < tolerance) {
- return 'ne';
- }
- // 左下角
- if (Math.abs(x - selection.x) < tolerance && Math.abs(y - (selection.y + selection.height)) < tolerance) {
- return 'sw';
- }
- // 右下角
- if (Math.abs(x - (selection.x + selection.width)) < tolerance && Math.abs(y - (selection.y + selection.height)) < tolerance) {
- return 'se';
- }
-
- return null;
- }
- // 鼠标事件处理
- imageCanvas.addEventListener('mousedown', (e) => {
- const rect = imageCanvas.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
-
- const handle = getResizeHandle(x, y);
- if (handle) {
- selection.isResizing = true;
- selection.resizeHandle = handle;
- selection.dragStartX = x;
- selection.dragStartY = y;
- } else if (isPointInSelection(x, y)) {
- selection.isDragging = true;
- selection.dragStartX = x - selection.x;
- selection.dragStartY = y - selection.y;
- imageCanvas.style.cursor = 'move';
- }
- });
- imageCanvas.addEventListener('mousemove', (e) => {
- const rect = imageCanvas.getBoundingClientRect();
- const x = e.clientX - rect.left;
- const y = e.clientY - rect.top;
-
- if (selection.isResizing) {
- const dx = x - selection.dragStartX;
- const dy = y - selection.dragStartY;
-
- switch (selection.resizeHandle) {
- case 'nw':
- selection.x += dx;
- selection.y += dy;
- selection.width -= dx;
- selection.height -= dy;
- break;
- case 'ne':
- selection.y += dy;
- selection.width += dx;
- selection.height -= dy;
- break;
- case 'sw':
- selection.x += dx;
- selection.width -= dx;
- selection.height += dy;
- break;
- case 'se':
- selection.width += dx;
- selection.height += dy;
- break;
- }
-
- // 确保最小尺寸
- if (selection.width < 20) selection.width = 20;
- if (selection.height < 20) selection.height = 20;
-
- selection.dragStartX = x;
- selection.dragStartY = y;
-
- drawSelection();
- } else if (selection.isDragging) {
- selection.x = x - selection.dragStartX;
- selection.y = y - selection.dragStartY;
-
- // 确保不超出边界
- if (selection.x < 0) selection.x = 0;
- if (selection.y < 0) selection.y = 0;
- if (selection.x + selection.width > imageCanvas.width) selection.x = imageCanvas.width - selection.width;
- if (selection.y + selection.height > imageCanvas.height) selection.y = imageCanvas.height - selection.height;
-
- drawSelection();
- } else {
- // 更新鼠标样式
- const handle = getResizeHandle(x, y);
- if (handle) {
- switch (handle) {
- case 'nw':
- case 'se':
- imageCanvas.style.cursor = 'nw-resize';
- break;
- case 'ne':
- case 'sw':
- imageCanvas.style.cursor = 'ne-resize';
- break;
- }
- } else if (isPointInSelection(x, y)) {
- imageCanvas.style.cursor = 'move';
- } else {
- imageCanvas.style.cursor = 'default';
- }
- }
- });
- imageCanvas.addEventListener('mouseup', () => {
- selection.isDragging = false;
- selection.isResizing = false;
- selection.resizeHandle = null;
- imageCanvas.style.cursor = 'default';
- });
- // 初始绘制
- img.onload = function() {
- imageCtx.drawImage(img, 0, 0, imageCanvas.width, imageCanvas.height);
- drawSelection();
- };
复制代码
8. 总结
本文详细介绍了使用JavaScript输出矩形的多种方法,从基础的HTML/CSS方法到高级的Canvas、SVG和WebGL技术。我们探讨了如何使用原生API以及各种JavaScript库来创建静态和动态矩形,并提供了丰富的代码示例和实际应用案例。
通过学习这些技术,你可以:
1. 使用HTML/CSS创建简单的矩形元素
2. 使用Canvas API绘制复杂的矩形图形和动画
3. 使用SVG创建可缩放的矢量矩形
4. 利用JavaScript库简化矩形绘制过程
5. 创建交互式矩形和动画效果
6. 优化矩形绘制的性能
7. 将矩形技术应用于数据可视化、游戏开发和图像处理等领域
掌握这些技能将大大提升你的前端开发能力,使你能够创建更加丰富和交互性强的Web应用。无论是简单的界面元素还是复杂的图形应用,矩形都是一个基础且重要的组成部分。
希望本文能够帮助你全面了解JavaScript输出矩形的各种方法和技巧,并在你的项目中灵活应用这些技术。 |
|