Create an Eraser with FabricJS in vuejs

81 Views Asked by At

I'm creating an image editor in vuejs with fabricjs. In the code below you can see we can already Paint and Add text to the image. Now I want to create an eraser which only erases the paint and not the image in the background. Currently I have this code:

<template>
    <div class="clipboard">
        <div class="clipboard-head">
            <div class="flex-center">
                <input class="clipboard-input" type="text">
                <div class="clipboard-btn">
                    <VButton
                        @clickHandler="$emit('convertToBlob', canvasEl)"
                        theme="light-blue"
                        active
                    >Save</VButton>
                    <VButton
                        theme="light-blue"
                    >Cancel</VButton>
                </div>
            </div>
            <div class="clipboard-controllers">
                <button @click="changeDrawingMode(true)" :class="['clipboard__text', { 'active': isBrushMode }]" style="width: auto;">Paint</button>
                <button @click="changeDrawingMode(false)" :class="['clipboard__text', { 'active': isTextMode }]" style="width: auto;">Select/Drag</button>
                <button @click="erase" :class="[{ 'active': isEraserMode }]" style="width: auto">Eraser</button>
                |
                <button @click="addText" class="clipboard__text-xl">Aa</button>
                <button @click="toggleBrush(5)">
                    <div class="clipboard__brash-xs brash"></div>
                </button>
                <button @click="toggleBrush(10)">
                    <div class="clipboard__brash-md brash"></div>
                </button>
                <button @click="toggleBrush(20)">
                    <div class="clipboard__brash-xl brash"></div>
                </button>
                <button @click="showClipboardColors =!showClipboardColors" class="clipboard__color">
                    <div class="clipboard__field" :style="{ backgroundColor: selectedColor }"></div>
                    <div v-if="showClipboardColors" class="clipboard__colors">
                        <button v-for="(color, index) in colors" @click="canvas.freeDrawingBrush.color = color, selectedColor = color">
                            <div
                                class="clipboard__field"
                                
                                :key="index"
                                :style="{ backgroundColor: color }"
                            ></div>
                        </button>
                    </div>
                </button>
            </div>
        </div>

        <div>
            <img class="clipboard-image" ref="image" :src="clipboard_img" alt="">
            <canvas :width="canvasWidth" :height="canvasHeight" ref="canvas"></canvas>
        </div>
    </div>
</template>

<script>
import VButton from "@/components/atoms/VButton";
import { fabric } from 'fabric';

export default {
    name: 'ImageEditor',
    components: { VButton },
    props: ['clipboard_img'],
    data() {
        return {
            showClipboardColors: false,
            canvasEl: null,

            canvas: null,
            canvasWidth: 300,
            canvasHeight: 300,
            isBrushMode: true,
            isTextMode: false,
            isEraserMode: false,
            selectedColor: '#000000',
            brushSize: 5

            colors: ['#ffffff', '#ff0000', '#00ff00', '#0000ff'],
        }
    },
    mounted() {
        setTimeout(() => {
            this.canvas = new fabric.Canvas(this.$refs.canvas, {
                selection: false,
            });

            this.canvas.isDrawingMode = this.isBrushMode;
            this.canvas.freeDrawingBrush.color = this.selectedColor;
            this.canvas.freeDrawingBrush.width = this.brushSize;
            this.canvas.on('path:created', (opt) => {
                if (this.isBrushMode) {
                    opt.path.globalCompositeOperation = 'source-over';
                } else if (this.isEraserMode) {
                    opt.path.globalCompositeOperation = 'destination-out';
                }
            });
            
            fabric.Image.fromURL(this.clipboard_img, (img) => {
                this.canvasWidth = this.$refs.image.width
                this.canvasHeight = this.$refs.image.height
                
                this.canvas.setBackgroundImage(img, this.canvas.renderAll.bind(this.canvas), {
                    scaleX: this.canvasWidth / img.width,
                    scaleY: this.canvasHeight / img.height,
                });

                this.canvas.setDimensions({ width: this.canvasWidth, height: this.canvasHeight });

                this.canvasEl = document.querySelector('.lower-canvas')
            });
        }, 100)
    },
    methods: {
        addText() {
            this.isBrushMode = false
            this.isTextMode = true
            this.isEraserMode = false

            const text = new fabric.IText('Type here', {
                left: 100,
                top: 100,
                fill: this.selectedColor
            });
            this.canvas.add(text);
            text.set({
                selectable: true,
            });
            this.canvas.setActiveObject(text)
            this.canvas.isDrawingMode = false;
        },
        toggleBrush(size) {
            this.isBrushMode = true
            this.isTextMode = false
            this.isEraserMode = false
            this.canvas.freeDrawingBrush.width = size;
            this.canvas.isDrawingMode = this.isBrushMode;
            this.canvas.selection = false;
        },
        erase() {
            this.isEraserMode = true
            this.isBrushMode = false;
            this.isTextMode = false;
            this.canvas.isDrawingMode = true;
            this.canvas.selection = false;
            this.selectedColor = '#ffffff'
            this.canvas.freeDrawingBrush.color = this.selectedColor
        },
        changeDrawingMode(type) {
            this.isBrushMode = type
            this.isTextMode = !type
            this.isEraserMode = false

            this.canvas.isDrawingMode = type
        }
    }
}
</script>

I used the code below but it erases everything, I tried adding the real image in the background so when we erase the canvas the image supports that part of the canvas and it does the job if it was just for drawing but I also need to convert the canvas to blob and upload etc...

this.canvas.on('path:created', (opt) => {
    if (this.isBrushMode) {
        opt.path.globalCompositeOperation = 'source-over';
    } else if (this.isEraserMode) {
        opt.path.globalCompositeOperation = 'destination-out';
    }
 });

I've spent the last 3 days trying to create and eraser but no luck so I'd appreciate if you could give me a hand. I know the code needs some polishing but after creating the eraser. Thank you in advance

0

There are 0 best solutions below