1. Sketchpad 2.0

Window drawing with Canvas -1.0

Rendering of 2.0

square

useSquare(){
    // Clear all tool event listeners as usual
    this.clearAllTool()
    if(this.toolStatus ! =3) {this.toolStatus = 3
        var x = 0,
            y = 0;
        var mouseMoveEvent = (e) = >{
            if(this.throttleDate==0 || this.throttleDate+16<Date.now()){
                // Clear the last doodle (undefined save), more on that later
                this.withdraw()
                // Get the width and height according to x and y
                this.ctx.strokeRect(x, y, e.layerX-x,e.layerY-y); }}this.squareEvents.down = (e) = > {
            x = e.layerX
            y = e.layerY
            // Use the graphics tool when retracting
            this.openShape = true
            this.ctx.strokeRect(x, y, 10.10);
            this.canvas.addEventListener('mousemove',mouseMoveEvent)
        }
        this.squareEvents.up = (e) = > {
            this.openShape = false
            this.canvas.removeEventListener('mousemove', mouseMoveEvent)
            this.save()
        }
        this.canvas.addEventListener('mousedown'.this.squareEvents.down);
        this.canvas.addEventListener('mouseup'.this.squareEvents.up)
    }else{
        / / close
        this.toolStatus = 0}},Copy the code

The idea is very similar to 1.0, except that the withdrawal function has been changed, and the withdrawal function is used frequently

circular

Now that we understand the circle, let’s look at the ellipse

Okay, so we’re not going to use circles, but it’s going to give you a sense of what an ellipse is, and we’re not going to use Angle offset, but we’re going to use everything else

useArc(){
    this.clearAllTool()
    if(this.toolStatus ! =4) {this.toolStatus = 4
        var x = 0,
            y = 0;
        var mouseMoveEvent = (e) = >{
            if(this.throttleDate==0 || this.throttleDate+16<Date.now()){
                this.withdraw()
                // Get XR and YR
                var radiusX = (e.layerX-x)/2,
                    radiusY = (e.layerY-y)/2;
                this.ctx.beginPath();
                // center of x = x+XR, y as above
                Math.pi = 0, 2 * math.pi
                this.ctx.ellipse(x+radiusX, y+radiusY, radiusX, radiusY, 0.0.2 * Math.PI);
                this.ctx.stroke()
            }
        }
        this.arcEvents.down = (e) = > {
            x = e.layerX
            y = e.layerY
            this.openShape = true
            this.ctx.beginPath();
            this.ctx.arc(x+10, y+10.10.0.2 * Math.PI, false);
            this.ctx.stroke()
            this.canvas.addEventListener('mousemove',mouseMoveEvent)
        }
        this.arcEvents.up = (e) = > {
            this.openShape = false
            this.canvas.removeEventListener('mousemove', mouseMoveEvent)
            this.save()
        }
        this.canvas.addEventListener('mousedown'.this.arcEvents.down);
        this.canvas.addEventListener('mouseup'.this.arcEvents.up)
    }else{
        / / close
        this.toolStatus = 0}},Copy the code

The logic of a circle is similar to that of a square, except that the formula for generating the circle is a little more complicated

Withdraw, save, restore (advance)

Since we have added the graphics tool, the current withdrawal requires frequent page refresh without changing the data, so it is improved

withdraw(){
    if(this.page > 0) {console.info('withdraw'.this.openShape)
        var img = new Image();
        // If you use the graphical tool, simply withdraw without adjusting the page count
        if(!this.openShape)
            this.page--
        img.src = this.saveRecords[this.page-1];
        console.info(this.saveRecords[this.page-1])
        this.ctx.clearRect(0.0.this.width, this.height);
        img.onload = () = >{
            this.ctx.drawImage(img, 0.0); }}}Copy the code

Undo is restore (Windows inside called restore, we now do as the Romans do)

recover(){
    console.info('recovery')
    if(this.page<this.saveRecords.length){
        // This variable is used to tell the save that I am restored and you can delete the page after it
        this.useRecover = true
        var img = new Image();
        this.page++
        img.src = this.saveRecords[this.page-1];
        this.ctx.clearRect(0.0.this.width, this.height);
        img.onload = () = >{
            this.ctx.drawImage(img, 0.0); }}},Copy the code

The logic of recovery is not difficult, is to take the cached image, or page, of the next page

“Save,” “retract,” “restore,” then our logic needs to be changed, too

save(){
    // If the archive is not withdrawn
    if(this.saveRecords.length == this.page){
        console.info('save')
        // Save directly
        this.saveRecords.push(this.canvas.toDataURL())
        this.page++;
    }else if(this.useRecover){
        console.info('recovery')
        // Delete the restored page
        for (let i = 0; i < this.saveRecords.length-this.page; i++) {
            this.saveRecords.pop()
        }
        // Start saving data
        This.page = this.saverecords. Length +1
        this.page = this.saveRecords.length
        this.page ++
        this.saveRecords.push(this.canvas.toDataURL())
        this.useRecover = false
    }else{
        console.info('Override cache')
        // If no, clear the archive after the current page
        for (let i = 0; i < this.saveRecords.length-this.page; i++) {
            this.saveRecords.pop()
        }
    }
}
Copy the code

Generally speaking, not much change, is added to restore to change

Second, complete code

<template>
    <div id="loon_canvas_editor">
        <canvas :ref="id" :width="width" :height="height"></canvas>
        <div id="toolbar">
            <img :class="toolStatus==1? 'select':''" @click="useBrush" src="https://wd-2019-1256239864.cos.ap-guangzhou.myqcloud.com/front/components/brush.png" alt="">
            <img :class="toolStatus==2? 'select':''" @click="useEraser" src="https://wd-2019-1256239864.cos.ap-guangzhou.myqcloud.com/front/components/eraser.png" alt="">
            <div :class="toolStatus==3? 'select shape':'shape'" @click="useSquare">
                <div style="width:15px; height:15px; border: 2px solid #39f;"></div>
            </div>
            <div :class="toolStatus==4? 'select arc':'arc'" @click="useArc">
                <div style="width:15px; height:15px; border: 2px solid #39f; border-radius:50%"></div>
            </div>
            
            <div class="interval"></div>
            <img @click="withdraw" src="https://wd-2019-1256239864.cos.ap-guangzhou.myqcloud.com/front/components/withdraw.png" alt="">
            <img @click="recover" style="transform: rotateY(180deg);" src="https://wd-2019-1256239864.cos.ap-guangzhou.myqcloud.com/front/components/withdraw.png" alt="">
            <img @click="save" src="https://wd-2019-1256239864.cos.ap-guangzhou.myqcloud.com/front/components/save.png" alt="">
        </div>
    </div>
</template>
<style lang="less" scoped>
    #loon_canvas_editor{
        width: 100%;
        height: 100%;
        font-size: 0;
        margin: 0 auto;
        canvas{
            border: 1px solid #39f;
        }
        #toolbar{
            display: flex;
            width: 300px;
            padding: 10px 20px;
            background: #deeeff;
            border: 1px solid #39f;
            margin-top: -1px;
            img{
                width: 20px;
                height: 20px;
                margin-right: 10px;
            }
            .shape{
                width: 20px;
                height: 20px;
                display: flex;
                align-items: center;
                justify-content: center;
                margin-right: 10px;
            }
            .arc{
                width: 20px;
                height: 20px;
                display: flex;
                align-items: center;
                justify-content: center;
                margin-right: 10px;
            }
            .select{
                border: 1px solid # 222;
            }
            .interval{
                width: 2px;
                height: 20px;
                background: #aaa;
                margin-right: 10px; }}}</style>
<script>
import { uuid } from 'uuidv4'
export default {
    name:'loonCanvasEditor'.props: ['width'.'height'].data(){
        return{
            id:uuid(),
            saveRecords: [].// Archive the history
            canvas:null.ctx:null.toolStatus:0.page:0.// pageIndex
            throttleBrush:null.// Brush throttling
            throttleDate: 0.brushEvents: {down:null.up:null
            },
            eraserEvents: {down:null.up:null
            },
            squareEvents: {down:null.up:null
            },
            arcEvents: {down:null.up:null
            },
            openShape:false.useRecover:false}},methods: {save(){
            // If the archive is not withdrawn
            if(this.saveRecords.length == this.page){
                console.info('save')
                // Save directly
                this.saveRecords.push(this.canvas.toDataURL())
                this.page++;
            }else if(this.useRecover){
                console.info('recovery')
                for (let i = 0; i < this.saveRecords.length-this.page; i++) {
                    this.saveRecords.pop()
                }
                this.page = this.saveRecords.length+1
                this.saveRecords.push(this.canvas.toDataURL())
                this.useRecover = false
            }else{
                console.info('Override cache')
                // If no, clear the archive after the current page
                for (let i = 0; i < this.saveRecords.length-this.page; i++) {
                    this.saveRecords.pop()
                }
            }
        },
        withdraw(){
            if(this.page > 0) {console.info('withdraw'.this.openShape)
                var img = new Image();
                if(!this.openShape)
                    this.page--
                img.src = this.saveRecords[this.page-1];
                console.info(this.saveRecords[this.page-1])
                this.ctx.clearRect(0.0.this.width, this.height);
                img.onload = () = >{
                    this.ctx.drawImage(img, 0.0); }}},recover(){
            console.info('recovery')
            if(this.page<this.saveRecords.length){
                this.useRecover = true
                var img = new Image();
                this.page++
                img.src = this.saveRecords[this.page-1];
                this.ctx.clearRect(0.0.this.width, this.height);
                img.onload = () = >{
                    this.ctx.drawImage(img, 0.0); }}},// Use a brush
        useBrush(){
            this.clearAllTool()
            if(this.toolStatus ! =1) {/ / open
                this.toolStatus = 1
                var mouseMoveEvent = (e) = >{
                    if(this.throttleDate==0 || this.throttleDate+16<Date.now()){
                        this.ctx.lineTo(e.layerX,e.layerY);
                        this.ctx.stroke(); }}this.brushEvents.down = (e) = > {
                    this.ctx.beginPath();
                    this.ctx.moveTo(e.layerX,e.layerY);
                    this.canvas.addEventListener('mousemove',mouseMoveEvent)
                }
                this.brushEvents.up = (e) = > {
                    this.canvas.removeEventListener('mousemove', mouseMoveEvent)
                    this.ctx.stroke();
                    this.save()
                }
                this.canvas.addEventListener('mousedown'.this.brushEvents.down);
                this.canvas.addEventListener('mouseup'.this.brushEvents.up)
            }else{
                / / close
                this.toolStatus = 0}},// Use an eraser
        useEraser(){
            this.clearAllTool()
            if(this.toolStatus ! =2) {this.toolStatus = 2
                var mouseMoveEvent = (e) = >{
                    if(this.throttleDate==0 || this.throttleDate+16<Date.now()){
                        this.ctx.clearRect(e.layerX-5,e.layerY-5.10.10); }}this.eraserEvents.down = (e) = > {
                    this.ctx.clearRect(e.layerX-5,e.layerY-5.10.10);
                    this.canvas.addEventListener('mousemove',mouseMoveEvent)
                }
                this.eraserEvents.up = (e) = > {
                    this.canvas.removeEventListener('mousemove', mouseMoveEvent)
                    this.save()
                }
                this.canvas.addEventListener('mousedown'.this.eraserEvents.down);
                this.canvas.addEventListener('mouseup'.this.eraserEvents.up)
            }else{
                / / close
                this.toolStatus = 0}},/ / use the box
        useSquare(){
            this.clearAllTool()
            if(this.toolStatus ! =3) {this.toolStatus = 3
                var x = 0,
                    y = 0;
                var mouseMoveEvent = (e) = >{
                    if(this.throttleDate==0 || this.throttleDate+16<Date.now()){
                        this.withdraw()
                        this.ctx.strokeRect(x, y, e.layerX-x,e.layerY-y); }}this.squareEvents.down = (e) = > {
                    x = e.layerX
                    y = e.layerY
                    this.openShape = true
                    this.ctx.strokeRect(x, y, 10.10);
                    this.canvas.addEventListener('mousemove',mouseMoveEvent)
                }
                this.squareEvents.up = (e) = > {
                    this.openShape = false
                    this.canvas.removeEventListener('mousemove', mouseMoveEvent)
                    this.save()
                }
                this.canvas.addEventListener('mousedown'.this.squareEvents.down);
                this.canvas.addEventListener('mouseup'.this.squareEvents.up)
            }else{
                / / close
                this.toolStatus = 0}},/ / circle
        useArc(){
            this.clearAllTool()
            if(this.toolStatus ! =4) {this.toolStatus = 4
                var x = 0,
                    y = 0;
                var mouseMoveEvent = (e) = >{
                    if(this.throttleDate==0 || this.throttleDate+16<Date.now()){
                        this.withdraw()
                        var radiusX = (e.layerX-x)/2,
                            radiusY = (e.layerY-y)/2;
                        this.ctx.beginPath();
                        this.ctx.ellipse(x+radiusX, y+radiusY, radiusX, radiusY, 0.0.2 * Math.PI);
                        this.ctx.stroke()
                    }
                }
                this.arcEvents.down = (e) = > {
                    x = e.layerX
                    y = e.layerY
                    this.openShape = true
                    this.ctx.beginPath();
                    this.ctx.arc(x+10, y+10.10.0.2 * Math.PI, false);
                    this.ctx.stroke()
                    this.canvas.addEventListener('mousemove',mouseMoveEvent)
                }
                this.arcEvents.up = (e) = > {
                    this.openShape = false
                    this.canvas.removeEventListener('mousemove', mouseMoveEvent)
                    this.save()
                }
                this.canvas.addEventListener('mousedown'.this.arcEvents.down);
                this.canvas.addEventListener('mouseup'.this.arcEvents.up)
            }else{
                / / close
                this.toolStatus = 0}},clearAllTool(){
            this.toolStatus = 0
            console.info('Clear all tools'.this.canvas.removeEventListener,this.brushEvents.down)
            this.canvas.removeEventListener('mousedown'.this.brushEvents.down)
            this.canvas.removeEventListener('mouseup'.this.brushEvents.up)
            this.canvas.removeEventListener('mousedown'.this.eraserEvents.down)
            this.canvas.removeEventListener('mouseup'.this.eraserEvents.up)
            this.canvas.removeEventListener('mousedown'.this.squareEvents.down)
            this.canvas.removeEventListener('mouseup'.this.squareEvents.up)
            this.canvas.removeEventListener('mousedown'.this.arcEvents.down)
            this.canvas.removeEventListener('mouseup'.this.arcEvents.up)
        }
    },
    mounted(){
        this.canvas = this.$refs[this.id]
        this.ctx = this.canvas.getContext('2d')

        this.canvas.style.width = this.width
        this.canvas.style.height = this.height

        this.save()
    }
}
</script>
Copy the code

That’s what I came to tell you this morning, but I don’t know how long the review will take. I’m writing the code