Vue趣味【Vue3+Element Plus+Canvas实现一个简易画板;支持导出为图片】

简介: Vue趣味【Vue3+Element Plus+Canvas实现一个简易画板;支持导出为图片】

🌟前言

哈喽小伙伴们,上一期给大家总结了一些常见的Vue实战中的经常用到的Vue小魔法,私下也收到了好多小伙伴的补充;小伙伴们实在是太强啦,和大家一起共同学习进步真的很开心。今天博主使用Vue3,结合ElementPlus和Canvas实现一个建议的画板,支持将绘画作品导出为图片哦;一起来看下吧。

🌟粉丝先看

2023-04-14 (5).png博主实在没有绘画细胞大家别笑

🌟创建Vue3项目

1.使用vite构建一个vue3项目


npm create vite@latest


7623d3c086d44e758b1df38293c0fe48.png

Project name:你的项目名;

Select a framework:框架选择Vue;

Select a variant:语言选择TypeSceript

cd vite-project

npm install

npm run dev

这样你的项目就创建好了

🌟引入Element Plus

Element-Plus官网国内站点

使用包管理器

我们建议您使用包管理器(如 NPM、Yarn 或 pnpm)安装 Element Plus,然后您就可以使用打包工具,例如 Vite 或 webpack。

# 选择一个你喜欢的包管理器
# NPM
$ npm install element-plus --save
# Yarn
$ yarn add element-plus
# pnpm
$ pnpm install element-plus

如果您的网络环境不好,建议使用相关镜像服务 cnpm 或 中国 NPM 镜像

浏览器直接引入

直接通过浏览器的 HTML 标签导入 Element Plus,然后就可以使用全局变量 ElementPlus 了。 根据不同的 CDN 提供商有不同的引入方式, 我们在这里以 unpkg 和 jsDelivr 举例。 你也可以使用其它的 CDN 供应商。

unpkg

<head>
  <!-- Import style -->
  <link rel="stylesheet" href="//unpkg.com/element-plus/dist/index.css" />
  <!-- Import Vue 3 -->
  <script src="//unpkg.com/vue@3"></script>
  <!-- Import component library -->
  <script src="//unpkg.com/element-plus"></script>
</head>

jsDelivr

<head>
  <!-- Import style -->
  <link
    rel="stylesheet"
    href="//cdn.jsdelivr.net/npm/element-plus/dist/index.css"
  />
  <!-- Import Vue 3 -->
  <script src="//cdn.jsdelivr.net/npm/vue@3"></script>
  <!-- Import component library -->
  <script src="//cdn.jsdelivr.net/npm/element-plus"></script>
</head>

完整引入

// main.ts
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import App from './App.vue'
const app = createApp(App)
app.use(ElementPlus)
app.mount('#app')

按需导入

首先你需要安装unplugin-vue-components 和 unplugin-auto-import这两款插件

npm install -D unplugin-vue-components unplugin-auto-import

然后把下列代码插入到你的 Vite 的配置文件中

// vite.config.ts
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
export default defineConfig({
  // ...
  plugins: [
    // ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

🌟实现代码(详细注释)

<template>
  <div id="content">
    <div class="toolBar">
      <el-button @click="clear">清空</el-button>
      <el-button @click="exprot">导出</el-button>
      <el-button @click="eraser">{{ text }}</el-button>
      <div style="margin-left: 12px">
        <!-- Element-Plus 颜色选择器 -->
        <el-color-picker  v-model="color1" @change="colorChange"/>
      </div>
      <div style="margin-left: 12px;width: 100px">
        <!-- Element-Plus 滑块 -->
        <el-slider style="margin-left: 12px;width: 100px" v-model="value1" @change="numberChange"/>
      </div>
    <!-- 右上方关闭按钮 -->
      <div class="light" @click="backIndex">
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        X
      </div>
    </div>
    <canvas id="myCanvas"></canvas>
  </div>
</template>
<script setup lang="ts">
  import {reactive, ref, toRefs, onBeforeMount, onMounted, nextTick} from 'vue'
  import {useRouter} from "vue-router";
  import icon from '../../assets/pen2.png'
  // 初始文字
  const text = ref('橡皮擦')
  // 该变量用来记录按钮文本
  const textFlag = ref(true)
  // 初始颜色
  const color1 = ref('#409EFF')
  // 初始线条粗细
  const value1 = ref(6)
  // 光标自定义
  const cursorIcon = ref(`url(${icon}),default`);
  let myCanvas: HTMLElement | null
  let ctx: { scale: (arg0: number, arg1: number) => void; moveTo: (arg0: number, arg1: number) => void; beginPath: () => void; lineWidth: any; strokeStyle: any; lineTo: (arg0: number, arg1: number) => void; stroke: () => void; }
  let isMouseDown: boolean
  let strokeStyle: string
  let lineWidth: number
  onMounted(() => {
    nextTick(() => {
      initCanvas()
    })
  })
  //返回首页
  const router = useRouter();
  function backIndex() {
    router.push('/')
  }
  // 初始化canvas
  function initCanvas() {
    // 线条细节处理,分辨率
    let dpr = window.devicePixelRatio || 1;
    myCanvas = document.getElementById('myCanvas')
    // 设置canvas实际尺寸
    myCanvas.width = window.innerWidth - 20
    myCanvas.height = window.innerHeight - 50
    // 让canvas坐标系统使用css像素
    ctx = myCanvas.getContext('2d');
    ctx.scale(dpr, dpr);
    // 监听canvas的鼠标按下时间
    myCanvas.addEventListener('mousedown', function (e) {
      // console.log(e);
      isMouseDown = true // 变量记录是否按下鼠标
      ctx.moveTo(e.pageX, e.pageY) // 将绘制起始点设置为鼠标按下的点
      // 设置绘制图形的样式:线条宽度和颜色;开始画线
      ctx.beginPath();
      ctx.lineWidth =  lineWidth || value1.value;
      ctx.strokeStyle = strokeStyle || color1.value;
    })
    // 监听鼠标松开事件
    myCanvas.addEventListener('mousemove', function (e) {
      if (isMouseDown) { // 如果鼠标按下
        ctx.lineTo(e.pageX, e.pageY) // 设置终点
        ctx.stroke() // 连接起点和终点并用设置好的样式描边
      }
    })
    // 监听鼠标松开事件,停止绘制
    myCanvas.addEventListener('mouseup', function () {
      // ctx.closePath();
      isMouseDown = false
    })
  }
  // 清空
  function clear() {
    // 1. 简单填充 使用一个新的背景色简单地填充整个画布,这样就可以清除当前内容
    // ctx.fillStyle = '#fff';
    // let rect = this.canvas.getBoundingClientRect();
    // ctx.fillRect(rect.x, rect.y, rect.width, rect.height)
    // 2.重置画布高度 当画布的宽或高被重置时,当前画布内容就会被移除。
    let rect = myCanvas.getBoundingClientRect();
    myCanvas.width = rect.width;
    myCanvas.height = rect.height;
    //3. 使用clearRect函数 clearRect() 函数可以指定起始点的x, y 位置以及宽度和高度来清除画布
    // let rect = this.canvas.getBoundingClientRect();
    // this.ctx.clearRect(rect.x, rect.y, rect.width, rect.height);
  }
  // 导出
  function exprot() {
    let link = document.createElement('a')
    link.href = myCanvas.toDataURL('image/png')
    link.download = 'draw.png'
    link.click()
  }
 // 改变颜色
  function colorChange(e: string | undefined) {
    console.log(e)
    strokeStyle = <string>e
  }
  // 改变线条粗细
  function numberChange(e: number) {
    lineWidth = e
  }
  // 橡皮擦,更换文本以及光标样式
  function eraser(e: string | undefined) {
    textFlag.value = !textFlag.value
    console.log(textFlag.value)
    if (!textFlag.value){
      cursorIcon.value = "url(/src/assets/rubber.png),default"
      text.value = '画笔'
      strokeStyle = '#ffffff'
    }else{
      cursorIcon.value = 'url(/src/assets/pen2.png),default'
      text.value = '橡皮擦'
      colorChange(e)
    }
  }
</script>
<style scoped lang="scss">
  html,
  body {
    width: 100%;
    height: 100%;
    margin: 0;
    box-sizing: border-box;
    overflow-y: hidden;
    overflow-x: hidden;
  }
  #content {
    width: 100%;
    height: 100%;
    position: relative;
  }
  .toolBar {
    width: 100vw;
    height: 50px;
    background-color: azure;
    box-shadow: 0 5px 2px #e8e8e8;
    position: absolute;
    top: 0;
    left: 0;
    display: flex;
    align-items: center;
    padding: 20px;
    box-sizing: border-box;
  }
   // 关闭按钮样式以及动画:霓虹灯效果
  .light {
    width: 40px;
    height: 30px;
    position: absolute;
    top: 30px;
    right: 30px;
    transform: translate(-50%, -50%);
    text-align: center;
    line-height: 30px;
    color: #03e9f4;
    font-size: 20px;
    text-transform: uppercase;
    transition: 0.5s;
    letter-spacing: 4px;
    cursor: pointer;
    overflow: hidden;
  }
  .light:hover {
    background-color: #03e9f4;
    color: #050801;
    box-shadow: 0 0 5px #03e9f4,
    0 0 25px #03e9f4,
    0 0 50px #03e9f4,
    0 0 200px #03e9f4;
  }
  .light div {
    position: absolute;
  }
  .light div:nth-child(1) {
    width: 100%;
    height: 2px;
    top: 0;
    left: -100%;
    background: linear-gradient(to right, transparent, #03e9f4);
    animation: animate1 1s linear infinite;
  }
  .light div:nth-child(2) {
    width: 2px;
    height: 100%;
    top: -100%;
    right: 0;
    background: linear-gradient(to bottom, transparent, #03e9f4);
    animation: animate2 1s linear infinite;
    animation-delay: 0.25s;
  }
  .light div:nth-child(3) {
    width: 100%;
    height: 2px;
    bottom: 0;
    right: -100%;
    background: linear-gradient(to left, transparent, #03e9f4);
    animation: animate3 1s linear infinite;
    animation-delay: 0.5s;
  }
  .light div:nth-child(4) {
    width: 2px;
    height: 100%;
    bottom: -100%;
    left: 0;
    background: linear-gradient(to top, transparent, #03e9f4);
    animation: animate4 1s linear infinite;
    animation-delay: 0.75s;
  }
  @keyframes animate1 {
    0% {
      left: -100%;
    }
    50%,
    100% {
      left: 100%;
    }
  }
  @keyframes animate2 {
    0% {
      top: -100%;
    }
    50%,
    100% {
      top: 100%;
    }
  }
  @keyframes animate3 {
    0% {
      right: -100%;
    }
    50%,
    100% {
      right: 100%;
    }
  }
  @keyframes animate4 {
    0% {
      bottom: -100%;
    }
    50%,
    100% {
      bottom: 100%;
    }
  }
  #myCanvas {
    cursor: v-bind(cursorIcon) ;
    overflow-y: hidden;
    overflow-x: hidden;
  }
</style>

🌟写在最后

以上就是实现简易画板的全部代码了,画出来的作品实在有点丑小伙伴们不要介意哈哈哈;大家也可以去试试,后续我会把一些图形加入进去实现拖拽等效果。喜欢的小伙伴们可以支持一下博主;你们的支持就是我创作的动力。各位小伙伴让我们 let’s be prepared at all times!



目录
相关文章
|
18天前
|
Web App开发 资源调度 JavaScript
vue element plus 安装
vue element plus 安装
30 0
|
4天前
|
JavaScript 前端开发
Vue鼠标悬浮切换图片
Vue鼠标悬浮切换图片
13 0
|
14天前
|
JavaScript
【vue】 element upload文件上传后表单校验信息还存在
【vue】 element upload文件上传后表单校验信息还存在
16 1
|
18天前
|
JavaScript
vue element 导出blob后台文件流xlsx文件自动下载(且规避乱码)
vue element 导出blob后台文件流xlsx文件自动下载(且规避乱码)
|
18天前
|
JavaScript
vue多条数据渲染(带图片)
vue多条数据渲染(带图片)
15 1
|
JavaScript 前端开发
vue开发:对Element上传功能的二次封装
最近公司老项目改用vue开发,前端框架采用element ui,这个框架风格还是很漂亮的,只是上传功能有一些问题,比如:limit=1限制上传数量后,后面的添加按钮没有隐藏,再用就是如果上传图片组,很多需求需要对图片组进行排序修改,基于这两个需求,对element的el-upload组件进行了二次封装。
2402 0
|
1天前
|
JavaScript
【vue】 父组件调用子组件方法
【vue】 父组件调用子组件方法
6 0
|
1天前
|
JavaScript
【vue】 如何给封装好的组件加click事件
【vue】 如何给封装好的组件加click事件
6 1
|
1天前
|
存储 JavaScript 前端开发
【Vue】绝了!这生命周期流程真...
【Vue】绝了!这生命周期流程真...
|
2天前
|
JavaScript 索引
【vue】框架搭建
【vue】框架搭建
6 1