javascripttypescriptvue.jsfabricjsimage-editing

Fabric.js IText interaction issue, moving but not editing


Trying to add IText component to the canvas that will be editable, resizable, rotatable etc.

It can be moved after adding, but cant be interacted in other way no matter what i do

fabric version - 5.3.0

vue.js + TS + Vite

here is my useCanvas composable

import { fabric } from 'fabric'
import { ref, Ref } from 'vue'
import { useElementSize } from '@vueuse/core'

const MIN_CANVAS_HEIGHT = 500

export function useCanvas(
  canvasRef: Ref<HTMLCanvasElement | null>,
  canvasContainerRef: Ref<HTMLElement | null>,
  minHeight = MIN_CANVAS_HEIGHT,
) {
  const canvas = ref<fabric.Canvas | null>(null)

  const { width: containerWidth, height: containerHeight } =
    useElementSize(canvasContainerRef)

  const init = () => {
    canvas.value = new fabric.Canvas(canvasRef.value, {
      width: containerWidth.value,
      height: minHeight,
    })
  }

  const addText = (value: string, isEditable = true) => {
    if (!canvas.value) return

    const text = new fabric.IText(value, {
      left: 50,
      top: 50,
      fill: '#000000',
      fontFamily: 'Arial',
      fontSize: 24,
      fontWeight: 'normal',
      editable: isEditable,
      centeredRotation: true,
      padding: 10,
    })

    // removing ability to scale without maintaining aspect ratio
    text.setControlsVisibility({
      mt: false,
      mb: false,
      ml: false,
      mr: false,
    })

    canvas.value.add(text)
  }
}

return {
    canvas,

    init,
    addText,
  }

and here is vue component where it is being used

template

 <template>
  <div ref="editorContainerRef" class="image-editor">
    <canvas ref="editorCanvasRef" class="image-editor__canvas" />
     <button class="button" type="button" @click="handleTextAdd">
      <span>{{ 'add text' }}</span>
    </button>
  </div>
</template>

script

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useCanvas } from './composables'

const DEFAULT_TEXT = 'Your unique signature'
const editorContainerRef = ref<HTMLElement | null>(null)
const editorCanvasRef = ref<HTMLCanvasElement | null>(null)

const canvasInstance = useCanvas(editorCanvasRef, editorContainerRef)

const { init, addText } = canvasInstance

const handleTextAdd = () => {
  addText(DEFAULT_TEXT)
}

onMounted(() => {
  if (!editorCanvasRef.value) return

  init()
})
</script>

As you can see i've already tried to set every property that could possibly affect scaling, rotating and editing ability but it doesn't work

But if i add more than one text to canvas and then select both, each of them begins to work as expected

So what i found is that text isn't interactable until you select it in group and i have no idea what causes that behavior


Solution

  • For those who will face same problem in the future:

    The problems goes from Vue reactivity that uses a lot of proxy under the hood, that in that particular case have some conflicts with fabric.js library and causes unexpected behavior

    So solution is: remove any reactivity from fabric.Canvas and any fabric.Object you are trying to interact with.

    Many hours of research leads to this small change

    doesn't work:

     const canvas = ref<fabric.Canvas | null>(null)
     canvas.value = new fabric.Canvas(canvasRef.value, {
      width: containerWidth.value,
      height: minHeight,
    })
    

    works as expected:

    let canvas = null as fabric.Canvas | null
    canvas = new fabric.Canvas(canvasRef.value, {
        width: containerWidth.value,
        height: minHeight,
      })