使用面向对象思想去封装实现canvas功能

前言

各种插件/库和一些常规的业务代码,最大的区别就在于编程的思路与方法。

比如我们现在想写一段业务代码,使用js实现一个矩形,那很简单,几行代码就可以了

        const canvas = document.getElementById('canvas')
        const mode = canvas.getContext('2d')
        mode.rect(200,200,200,200)
        mode.fillStyle = "red"
        mode.fill()  

因为功能非常简单,我们就按照功能的思路很快实现

但是,如果我们需要去实现一个插件库,你这么写代码,可就不行了。

别人要使用你的插件,肯定是不需要从头看你的源码,你需要将自己封装好的api暴露给用户。

现在用户希望迅速创建一个矩形,他肯定不会像原生js一样去写

理想状态应该是这样子的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>自定义封装canvas插件</title>
</head>
<body>
    <canvas id="mycanvas"></canvas>
    <script src="../myCanvasPlugins.js"></script>
    <script>
        const myCanvas = new CanvasPlugin().instance('#mycanvas',{width:800,height:600})
        let canvasDataList = [
            {
                type:'rect',
                nodeName:'rect1',
                x:100,
                y:100,
                width:100,
                height:100,
                styleType:'fill',
                strokeStyle:null,
                fillStyle:'red',
                event:{
                    click:()=>{
                        console.log('点击了rect1元素')
                    }
                }
            },
            {
                type:'arc',
                nodeName:'arc1',
                x:300,
                y:300,
                radius:100,
                start:0,
                end:360,
                direction:true,
                styleType:'stroke',
                strokeStyle:'blue',
                fillStyle:null,
                event:{
                    click:()=>{
                        console.log('点击了arc1元素')
                    }
                }
            }
        ]
        myCanvas.draw(canvasDataList)
    </script>
</body>
</html>

所以通过分析,我们的myCanvasPlugin是需要完成一个公共的方法可以供用户使用

所以现在我们需要去实现一个js的插件,他可以让用户直接简单的去使用

思路

1.创建实例,使用的是new,所以这里我们最外层需要暴露的肯定是一个构造函数或者类

2.new之后的实例对象调用instance方法需要用户传入一个id号和元素的宽高,所以我们肯定需要在内部对dom进行一个封装

3.我们需要设置固定的属性配置,可以让用户去根据配置项,配置生成理想的数据

4.需要在实例上设置draw方法,将用户配置好的数据传入,完成内部的渲染

思路捋出来的,后续的函数设计就很明白了

而且为了批量化的操作,我们需要大量使用面向对象编程思想

实现

代码如下

/** 生成canvas的类 */
class CanvasPlugin{
    constructor(){
        this.instance = (id,nodeInfo)=>{
             const node =  document.querySelector(id)
             node.setAttribute('width',nodeInfo.width)
             node.setAttribute('height',nodeInfo.height)
             node.draw = this.draw
             return node
        }
    }
    draw(list){
        let useDataList= []
        list.forEach(item=>{
            let node = null
            switch(item.type){
             case 'rect':
               node = new Rect(item.nodeName,item.x,item.y,item.width,item.height,item.styleType,item.strokeStyle,item.fillStyle,item.event)
             break
             case 'arc':
               node = new Arc(item.nodeName,item.x,item.y,item.radius,item.start,item.end,item.direction,item.styleType,item.strokeStyle,item.fillStyle,item.event)
            }
            useDataList.push(node)
        })
        console.log(this,'???this---')
        useDataList.forEach(item=>{
            item.drawFun(this)
        })
    }
}

/** 生成矩形的类 */
class Rect{
    constructor(nodeName,x,y,width,height,styleType,strokeStyle,fillStyle,event) {
        this.type = 'rect'
        this.nodeName = nodeName
        this.x = x
        this.y = y
        this.width = width
        this.height = height
        this.styleType = styleType
        this.strokeStyle = strokeStyle
        this.fillStyle = fillStyle
        this.event = event
    }
    drawFun(canvasNode){
        const ctx = canvasNode.getContext('2d')
        let eventKeyList = Object.keys(this.event)
        let fn = (ctx,canvasPosition,$event,event)=>{
            ctx.beginPath()
            ctx[this.type](this.x,this.y,this.width,this.height)
            ctx[`${this.styleType}Style`] = this[[`${this.styleType}Style`]]
            ctx[`${this.styleType}`]()
            ctx.closePath()
            if(canvasPosition){
                const isBeTrigger = ctx.isPointInPath(canvasPosition.x, canvasPosition.y)
                if (isBeTrigger === true) {
                    event()
                }
            }
        }
        fn(ctx)
        commonEventHandler(ctx,eventKeyList,canvasNode,fn,this.event)
    }
}

/** 生成圆形的类*/
class Arc{
constructor(nodeName,x,y,radius,start,end,direction,styleType,strokeStyle,fillStyle,event){
         this.type = 'arc'
         this.nodeName = nodeName
         this.x = x
         this.y = y
         this.radius = radius
         this.start = start
         this.end = end
         this.direction = direction
         this.styleType = styleType
         this.strokeStyle = strokeStyle
         this.fillStyle = fillStyle
         this.event = event
    }
    drawFun(canvasNode){
        const ctx = canvasNode.getContext('2d')
        let eventKeyList = Object.keys(this.event)
        let fn = (ctx,canvasPosition,$event,event)=>{
            ctx.beginPath()
            ctx[this.type](this.x,this.y,this.radius,[Math.PI/180]*this.start,[Math.PI/180]*this.end,this.direction)
            ctx[`${this.styleType}Style`] = this[[`${this.styleType}Style`]]
            ctx[`${this.styleType}`]()
            ctx.closePath()
            if(canvasPosition){
                const isBeTrigger = ctx.isPointInPath(canvasPosition.x, canvasPosition.y)
                if (isBeTrigger === true) {
                    event()
                }
            }
        }
        fn(ctx)
        commonEventHandler(ctx,eventKeyList,canvasNode,fn,this.event)
    }
}

/**
 * @function 公共方法
 * @description 给画布添加dom事件监听,并且会绘制该节点。canvas中元素的事件绑定和触发不同于常规的dom
 * @author 王惊涛
 * @param ctx 可操作做的画布实例
 * @param eventKeyList 事件属性列表:例如['click','mouseover']
 * @param canvasNode canvas的dom元素,这里主要用于判定canvas节点的具体位置
 * @param fn 当前节点的绘制函数,定义在类中
 * @param event 用户根据当前目标自定义的事件
*/
function commonEventHandler(ctx,eventKeyList,canvasNode,fn,event){
    eventKeyList.forEach(item=>{
        canvasNode.addEventListener(item,($event)=>{
          let position = canvasNode.getBoundingClientRect()
          let canvasPosition = {
            x: $event.clientX - position.left,
            y: $event.clientY - position.top
          }
          fn(ctx,canvasPosition,$event,event[item])
        })
    })
}

这里对于有些读者可能会感觉到比较绕一些,但是,听我一言。

如果你真的需要去封装一些什么东西,并且想做一些自己的插件这类的,这种操作是你必须经历的。而且一定要认真地去阅读代码,并且要多复盘几次!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/416518.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CIM的基本概念知识,以及应用场景介绍

CIM的理念 CIM基本理念总结为&#xff1a;依托先进技术、面向具体需求、融合动态信息、描述实体单元、支撑各类应用。 首先要依托于对 BIM、GIS、IOT技术的透彻理解和深度应用&#xff0c;并与云计算、大数据等技术充分融合。 同时&#xff0c;针对不同区域、不同运行管理对象、…

【dc-dc】AP5101C 高压线性恒流IC 9-19V 500ma LED电源驱动方案 过压保护 抗浪涌线路

产品描述 AP5101C 是一款高压线性 LED 恒流芯片 &#xff0c; 外围简单 、 内置功率管 &#xff0c; 适用于6- 100V 输入的高精度降压 LED 恒流驱动芯片。最大电流2.0A。AP5101C 可实现内置MOS 做 2.0A,外置 MOS 可做 3.0A 的。AP5101C 内置温度保护功能 &#xff0c;温度保护…

拜登:“一切非 Rust 项目均为非法”,开发界要大变天?

文章目录 科技巨头应为安全漏洞负起责任使用其他语言的开发者​该何去何从&#xff1f; 白宫国家网络总监办公室&#xff08;ONCD&#xff0c;以下简称网总办&#xff09;在本周一发布的报告中说道&#xff1a;“程序员编写代码并非没有后果&#xff0c;他们的⼯作⽅式于国家利…

练习 2 Web [ACTF2020 新生赛]BackupFile 1

[ACTF2020 新生赛]BackupFile 1 Web常规题目 首先尝试查找常见的前端页面index.php之类的&#xff0c;没找到 题目有个“BackupFile”——备份文件 尝试用工具遍历查找相关的文件 御剑没扫出来&#xff0c;搜索搭建好dirsearch后&#xff0c;扫出来的index.php.bak 扫描工…

我又上来啦!湖人胜率反超勇士升至第九 勇士降第十

nba直播吧 02月29日讯 湖人今日在洛杉矶德比中116-112逆转击沉快船&#xff01; 赛后&#xff0c;湖人的战绩来到32胜28负&#xff0c;胜率反超勇士&#xff08;30胜27负&#xff09;&#xff0c;升至西部第九&#xff0c;勇士降到西部第十。

eBay美国站怎么样?eBay跨境电商卖家开店必知!

在众多跨境电商平台之中&#xff0c;eBay作为全球知名的电商平台之一&#xff0c;为卖家提供了跨境电商的机会。对于中国卖家而言&#xff0c;这是一个充满机遇的市场。那么&#xff0c;eBay美国站怎么样&#xff1f;如何顺利在美国eBay上开店呢&#xff1f;让我们一起来了解一…

Apache SeaTunnel 及 Web 功能部署指南(小白版)

在大数据处理领域&#xff0c;Apache SeaTunnel 已成为一款备受青睐的开源数据集成平台&#xff0c;它不仅可以基于Apache Spark和Flink&#xff0c;而且还有社区单独开发专属数据集成的Zeta引擎&#xff0c;提供了强大的数据处理能力。随着SeaTunnel Web的推出&#xff0c;用户…

证明StringBuilder不是现性安全的

1 理论证明 StringBuilder存放字符串的属性是char value[], 这在其父类AbstractStringBuilder里面可以查看&#xff1a; 数组都是创建在堆中&#xff0c;堆中的内存任意线程都可以访问。而且这个字符数组没有像String类那样用final修饰&#xff0c;所以任意线程都可以修改&am…

八、ActiveMQ持久化

ActiveMQ持久化 一、MQ的高可用二、持久化介绍三、持久化存储方式1.AMQ Mesage Store(了解&#xff09;2.KahaDB消息存储(默认)2.1 存储原理 3.JDBC消息存储4.LevelDB消息存储(了解)5.JDBC Message Store with ActiveMQ Journal查询持久化存储方式 四、持久化存储使用1.JDBC消息…

【小夏送书 | 第二期】世界顶级名校计算机专业,都在用哪些书当教材?

&#x1f304;参与规则 参与方式&#xff1a;关注博主点赞收藏评论&#xff0c;&#xff08;每人最多评论三次&#xff09; 本次送书1~3本【取决于阅读量&#xff0c;阅读量越多&#xff0c;送的越多】 活动时间至&#xff1a;2024-3-6 20:00:00 | 随机抽取由博主动态公布抽…

linux centos 挂载磁盘

linux centos 挂载磁盘 未挂载磁盘分区挂载硬盘 背景&#xff1a;客户新加了一块600G的数据盘要挂载使用 未挂载磁盘分区 查看未挂载的磁盘 fdisk -l开始分区 fdisk /dev/sdb按照图示一次输入指令 再次查看 fdisk -l格式化 mkfs -t ext4 /dev/sdb1挂载硬盘 创建数据目录…

Visio使用笔记

记录一下需要的常用操作 圆角矩形的弧度调整 如果一开始创建的是圆角矩形&#xff0c;可以菜单栏先改为矩形 取消箭头的对齐吸附 alt F9&#xff0c;取消勾选 忘记快捷键&#xff0c;可以菜单栏找到视图&#xff0c;点击视觉帮助右下角的小箭头&#xff0c;引出上面的界面…

《Decoupling Representation and Classifier for Long-Tailed Recognition》阅读笔记

论文标题 《Decoupling Representation and Classifier for Long-Tailed Recognition》 用于长尾识别的解耦表示和分类器 作者 Bingyi Kang、Saining Xie、Marcus Rohrbach、Zhicheng Yan、 Albert Gordo、Jiashi Feng 和 Yannis Kalantidis 来自 Facebook AI 和 新加坡国…

推荐一款桌面端redis连接工具, redis desktop manager替代品——another redis desktop manager

下载地址 Another Redis Desktop Manager | 更快、更好、更稳定的Redis桌面(GUI)管理客户端&#xff0c;兼容Windows、Mac、Linux&#xff0c;性能出众&#xff0c;轻松加载海量键值 封面对比 对比redis desktop manager &#xff0c;ui上有巨大的改进 但是redis desktop ma…

力扣262 行程和用户

如何计算特定时间段内非禁止用户的出租车行程取消率。这个问题可以通过SQL查询来解决&#xff0c;我们需要关联Trips表和Users表来筛选出符合条件的行程记录&#xff0c;并计算取消率。 目录 题目描述 思路整理 完整代码及解释 题目描述 表&#xff1a;Trips -----------…

数据卷(Data Volumes)自定义镜像(Dockerfile)

目录 一.数据卷(Data Volumes) 二.自定义镜像(Dockerfile) 自定义centos 一.数据卷(Data Volumes) 数据卷(Data Volumes)是一个可供一个或多个容器使用的特殊目录&#xff0c;它将主机操作系统目录直接映射进容器&#xff0c;它可以提供很多有用的特性&#xff1a; 1.数据卷…

软件测试如何做到充分性测试?

做软件测试要想保质保量&#xff0c;就要做到测试充分&#xff0c;什么是测试充分&#xff0c;就是把所需要覆盖的场景都要覆盖到。如何做到场景全面覆盖&#xff0c;特别是在时间紧任务重的时候&#xff1f;我把我这些年来工作的一点经验总结一下分享给大家&#xff0c;希望对…

基于springboot实现二手图书交易平台系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现二手图书交易平台系统演示 摘要 本文讲述了基于B/S模式的校园二手交易网站统的设计与实现。所谓的校园二手交易网站统是通过网站推广互联企业的二手物品和技术服务&#xff0c;并使客户随时可以了解企业和企业的产品&#xff0c;为客户提供在线服务和订单处…

ctfshow——反序列化

文章目录 web 254——啥也没web 255——反序列化对变量进行赋值&#xff08;1&#xff09;web 256——反序列化对变量进行赋值&#xff08;2&#xff09;web 257——对象注入web 258——对象注入(绕过preg_match)web 259 web 254——啥也没 这里就是使用GET传输&#xff0c;use…

Python光速入门 - Flask轻量级框架

FlASK是一个轻量级的WSGI Web应用程序框架&#xff0c;Flask的核心包括Werkzeug工具箱和Jinja2模板引擎&#xff0c;它没有默认使用的数据库或窗体验证工具&#xff0c;这意味着用户可以根据自己的需求选择不同的数据库和验证工具。Flask的设计理念是保持核心简单&#xff0c…