likes
comments
collection
share

快来看看使用React构建的交互式绘图应用效果

作者站长头像
站长
· 阅读数 41

前言

在如今数字化时代,Web应用程序已成为了人们日常生活的一部分。绘图应用是其中一种常见的应用类型,他可以让用户在Web浏览器中进行绘图和创作。本文将介绍如何使用React构建一个简单的绘图应用,通过该应用,用户可以在画布上绘制图形、更改画笔尺寸和颜色,以及清空画布等功能。

背景

本文实现的绘图应用的构建是基于React框架。众所周知,React是一个用于构建用户界面的Javascript库,它提供了组件化的开发方式,使得构建复杂的交互方式应用更加高效和可维护。在本项目中,将使用React的状态管理和事件处理功能来实现绘图应用的核心功能。

开发

那话不多说,下面就一起来看看该项目的实现过程,请往下瞧→

首先,需要引入React库,并在组件中引入所需的Hooks和样式文件。如下:

import React, { useRef, useState } from 'react';
import './index.less';

至于样式文件,这里就把已经写好的代码贴出来算了,后续自行修改相关样式即可。如下:

* {
  box-sizing: border-box;
}

canvas {
  border: 2px solid #34516a;
}

.toolbox {
  display: flex;
  width: 904px;
  padding: 10px;
  background-color: #34516a;
  border: 1px solid #34516a;
}

.toolbox > * {
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28px;
  height: 50px;
  width: 50px;
  margin: 4px;
  padding: 4px;
  cursor: pointer;
  color: #333;
  background-color: #fff;
  border: none;
}

.toolbox > *:last-child {
  margin-left: auto;
}

在函数组件DrawingApp中,需要定义一些状态变量,用于存储画笔尺寸、颜色、鼠标状态和鼠标位置等信息。然后通过使用React的useStateHooks,就可以在函数组件中定义和更新这些状态。代码如下:

const DrawingApp = () => {
  const canvasRef = useRef(null);
  const [size, setSize] = useState(10);
  const [color, setColor] = useState('#ff0000');
  const [isPressed, setIsPressed] = useState(false);
  const [prevX, setPrevX] = useState(null);
  const [prevY, setPrevY] = useState(null);

接下来,需要编写处理鼠标事件的函数。当鼠标按下时,记录鼠标的位置,并将isPressed的状态设置为true,表示鼠标当前处于按下状态。代码如下:

/**
 * 鼠标按下
 * @param e
 */
const handleMouseDown = (e) => {
  setIsPressed(true);
  const canvas = canvasRef.current;
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;
  setPrevX(x);
  setPrevY(y);
};

当鼠标松开时,将isPressed的状态设置为false,表示鼠标当前处于未按下状态,并清空之前记录的鼠标位置。代码如下:

/**
 * 鼠标松开
 */
const handleMouseUp = () => {
  setIsPressed(false);
  setPrevX(null);
  setPrevY(null);
};

在鼠标移动时,需要判断鼠标是否处于按下状态。如果是,则获取当前鼠标的位置,并调用drawCircledrawLine函数进行绘制操作。绘制圆形和绘制直线的具体代码如下:

/**
 * 移动鼠标
 * @param e
 */
const handleMouseMove = (e) => {
  if (!isPressed) return;

  const canvas = canvasRef.current;
  const rect = canvas.getBoundingClientRect();
  const x = e.clientX - rect.left;
  const y = e.clientY - rect.top;

  drawCircle(x, y);
  drawLine(prevX, prevY, x, y);

  setPrevX(x);
  setPrevY(y);
};


/**
 * 画圆角
 * @param x
 * @param y
 */
const drawCircle = (x, y) => {
  const canvas = canvasRef.current;
  const ctx = canvas.getContext('2d');
  ctx.beginPath();
  ctx.arc(x, y, size, 0, Math.PI * 2);
  ctx.fillStyle = color;
  ctx.fill();
};


/**
 * 画线
 * @param x1
 * @param y1
 * @param x2
 * @param y2
 */
const drawLine = (x1, y1, x2, y2) => {
  const canvas = canvasRef.current;
  const ctx = canvas.getContext('2d');
  ctx.beginPath();
  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.strokeStyle = color;
  ctx.lineWidth = size * 2;
  ctx.stroke();
};

为了使该应用的体验更好,还提供了一些额外的功能。通过调用increaseSizedecreaseSize函数,用户可以增加或减小画笔的尺寸。这里限制了尺寸的取值范围在5~50之间。同时,将尺寸大小实时地显示在界面上。代码如下:

/**
 * 更新屏幕尺寸
 */
const updateSizeOnScreen = () => {
  const sizeEl = document.getElementById('size');
  if (sizeEl) {
    sizeEl.innerText = size;
  }
};


/**
 * 减小画笔尺寸
 */
const increaseSize = () => {
  let newSize = size + 5;
  if (newSize > 30) {
    newSize = 30;
  }
  setSize(newSize);
  updateSizeOnScreen();
};


/**
 * 增加画笔尺寸
 */
const decreaseSize = () => {
  let newSize = size - 5;
  if (newSize < 5) {
    newSize = 5;
  }
  setSize(newSize);
  updateSizeOnScreen();
};

此外,还提供了一个颜色选择器,可以让用户自由地选择画笔颜色。通过调用handleColorChange函数,可以更新当前选择的颜色。代码如下:

/**
 * 更改颜色
 * @param e
 */
const handleColorChange = (e) => {
  setColor(e.target.value);
};

最后,还提供了一个清空画布的功能,通过调用clearCanvas函数可以清除整个画布的内容。代码如下:

/**
 * 清除画布
 */
const clearCanvas = () => {
  const canvas = canvasRef.current;
  const ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, canvas.width, canvas.height);
};

到这里,该应用的核心代码就介绍完了。下面就是福利时刻,完整的代码如下:

import React, { useRef, useState } from 'react';
import './index.less';

const DrawingApp = () => {
  const canvasRef = useRef(null);
  const [size, setSize] = useState(10);
  const [color, setColor] = useState('#ff0000');
  const [isPressed, setIsPressed] = useState(false);
  const [prevX, setPrevX] = useState(null);
  const [prevY, setPrevY] = useState(null);

  /**
   * 鼠标按下
   * @param e
   */
  const handleMouseDown = (e) => {
    setIsPressed(true);
    const canvas = canvasRef.current;
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    setPrevX(x);
    setPrevY(y);
  };


  /**
   * 鼠标松开
   */
  const handleMouseUp = () => {
    setIsPressed(false);
    setPrevX(null);
    setPrevY(null);
  };


  /**
   * 移动鼠标
   * @param e
   */
  const handleMouseMove = (e) => {
    if (!isPressed) return;

    const canvas = canvasRef.current;
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    drawCircle(x, y);
    drawLine(prevX, prevY, x, y);

    setPrevX(x);
    setPrevY(y);
  };

  /**
   * 画圆角
   * @param x
   * @param y
   */
  const drawCircle = (x, y) => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.beginPath();
    ctx.arc(x, y, size, 0, Math.PI * 2);
    ctx.fillStyle = color;
    ctx.fill();
  };


  /**
   * 画线
   * @param x1
   * @param y1
   * @param x2
   * @param y2
   */
  const drawLine = (x1, y1, x2, y2) => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.strokeStyle = color;
    ctx.lineWidth = size * 2;
    ctx.stroke();
  };


  /**
   * 更新屏幕尺寸
   */
  const updateSizeOnScreen = () => {
    const sizeEl = document.getElementById('size');
    if (sizeEl) {
      sizeEl.innerText = size;
    }
  };


  /**
   * 减小画笔尺寸
   */
  const increaseSize = () => {
    let newSize = size + 5;
    if (newSize > 30) {
      newSize = 30;
    }
    setSize(newSize);
    updateSizeOnScreen();
  };


  /**
   * 增加画笔尺寸
   */
  const decreaseSize = () => {
    let newSize = size - 5;
    if (newSize < 5) {
      newSize = 5;
    }
    setSize(newSize);
    updateSizeOnScreen();
  };


  /**
   * 更改颜色
   * @param e
   */
  const handleColorChange = (e) => {
    setColor(e.target.value);
  };


  /**
   * 清除画布
   */
  const clearCanvas = () => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0, canvas.width, canvas.height);
  };

  return (
      <div>
        <div className="toolbox">
          <button onClick={decreaseSize}>-</button>
          <span id="size">{size}</span>
          <button onClick={increaseSize}>+</button>
          <input type="color" id="color" onChange={handleColorChange} value={color} />
          <button onClick={clearCanvas}>X</button>
        </div>
        <canvas
            ref={canvasRef}
            width="900"
            height="600"
            onMouseDown={handleMouseDown}
            onMouseUp={handleMouseUp}
            onMouseMove={handleMouseMove}
        ></canvas>
      </div>
  );
}

export default DrawingApp;

将上述代码复制到React项目中并运行,你可以得到如下效果:

快来看看使用React构建的交互式绘图应用效果

总结一下,本文介绍了如何使用React构建绘图应用,并详细解释了应用中的核心代码。使用了useRef来引用canvas元素,useState来管理状态变量,包括画笔尺寸、颜色、鼠标状态和位置等。通过鼠标事件处理函数,实现了绘制圆形和直线的功能,并在鼠标移动时不断更新画布。此外,还提供了调整画笔尺寸、选择画笔颜色和清空画布等额外功能,以提升用户体验。

通过本文的介绍,你可以根据提供的代码和思路,进一步扩展和定制绘图应用,以满足实际开发中的更多需求。

后语

小伙伴们,如果觉得本文对你有些许帮助,点个👍或者➕个关注再走吧^_^ 。另外如果本文章有问题或有不理解的部分,欢迎大家在评论区评论指出,我们一起讨论共勉。

转载自:https://juejin.cn/post/7242203662415216698
评论
请登录