NeuroAgent

Fix Fabric.js Line Display Issues When Loading from PHP

Fix Fabric.js line display issues when loading from PHP via JSON. Learn how to make lines show initially instead of only when canvas is selected.

Question

How to create a drawing from PHP in Fabric.js and solve the line display issue?

The drawing logic is created on the PHP side, then passed via JSON to fabric.js for display. I created a line, but it only displays when the canvas is selected. I need it to display initially.

PHP code for creating the line:

php
$crease_1 = [
    'type'        => 'line',
    // Relative coordinates of points
    'x1'          => 0,     // relative to left
    'y1'          => 0,     // relative to top
    'x2'          => 200,   // relative to left
    'y2'          => 0,     // relative to top
    'stroke'      => 'black', // line color
    'strokeWidth' => 2,     // line thickness
];

HTML with canvas:

html
<canvas id="canvas" width="800" height="600" data-dieline="{{ json_encode($dieline) }}"></canvas>

JavaScript for loading and displaying:

javascript
import * as fabric from 'fabric';

// Load data
let dataCanvas = document.getElementById('canvas');
let dieline    = dataCanvas.dataset.dieline;
dieline        = JSON.parse(dieline);

const canvas = new fabric.Canvas('canvas');

canvas.loadFromJSON(dieline, function() {
    canvas.renderAll();
});

Example JSON from console:

json
{
    "objects": [
        {
            "type": "group",
            "left": 100,
            "top": 100,
            "objects": []
        },
        {
            "type": "line",
            "x1": 0,
            "y1": 0,
            "x2": 200,
            "y2": 0,
            "stroke": "black",
            "strokeWidth": 2
        }
    ]
}

How can I fix the issue so the line displays initially, not only when the canvas is selected?

NeuroAgent

Why Lines Only Appear When Canvas is Selected in Fabric.js

The reason a line only appears when the canvas is selected rather than initially is related to how Fabric.js works when loading objects from JSON. This is a common problem that occurs because some objects require additional initialization or proper timing for rendering.

Contents

Main causes of the problem

The problem with displaying lines when loading from JSON in Fabric.js occurs for several reasons:

  1. Asynchronous loading issues - as mentioned in issue #9792, using callbacks can be unreliable in newer versions of Fabric.js
  2. Incorrect coordinates - lines require absolute coordinates, not relative ones
  3. Insufficient initialization - some objects require additional rendering after loading
  4. Version-specific features - as mentioned in issue #184, different versions of Fabric.js handle callbacks differently

Solution 1: Using Promise instead of callback

The most reliable solution is to use Promise instead of callbacks, as recommended in several issues:

javascript
import * as fabric from 'fabric';

let dataCanvas = document.getElementById('canvas');
let dieline = dataCanvas.dataset.dieline;
dieline = JSON.parse(dieline);

const canvas = new fabric.Canvas('canvas');

// Use Promise instead of callback
canvas.loadFromJSON(dieline)
    .then(() => {
        canvas.renderAll();
        console.log('Objects loaded successfully');
    })
    .catch(error => {
        console.error('Loading error:', error);
    });

This solution works more reliably because, as noted in issue #9792, using .then() blocks ensures proper code execution after loading.


Solution 2: Explicitly setting line coordinates

The main issue might be that lines in Fabric.js require absolute coordinates, not relative ones. Your PHP code uses relative coordinates, which can cause display issues:

php
// Fixed PHP code with absolute coordinates
$crease_1 = [
    'type'        => 'line',
    'left'        => 100,    // absolute X position
    'top'         => 100,    // absolute Y position
    'x1'          => 0,      // relative coordinate within object
    'y1'          => 0,
    'x2'          => 200,    // relative coordinate within object
    'y2'          => 0,
    'stroke'      => 'black',
    'strokeWidth' => 2,
];

Or an alternative approach with direct line creation:

php
// Alternative way to create a line
$crease_1 = [
    'type'        => 'line',
    'x1'          => 100,    // absolute X coordinate of first point
    'y1'          => 100,    // absolute Y coordinate of first point
    'x2'          => 300,    // absolute X coordinate of second point
    'y2'          => 100,    // absolute Y coordinate of second point
    'stroke'      => 'black',
    'strokeWidth' => 2,
];

Solution 3: Additional initialization

Sometimes additional object initialization is required after loading:

javascript
import * as fabric from 'fabric';

let dataCanvas = document.getElementById('canvas');
let dieline = dataCanvas.dataset.dieline;
dieline = JSON.parse(dieline);

const canvas = new fabric.Canvas('canvas');

canvas.loadFromJSON(dieline, function() {
    canvas.renderAll();
    
    // Additional line initialization
    canvas.getObjects().forEach(function(obj) {
        if (obj.type === 'line') {
            obj.selectable = false; // disable selection
            obj.evented = false;    // disable events
        }
    });
    
    canvas.renderAll(); // re-render
});

Solution 4: Controlling loading order

The issue might be related to the canvas initialization order. Make sure the canvas is fully initialized before loading data:

javascript
import * as fabric from 'fabric';

document.addEventListener('DOMContentLoaded', function() {
    let dataCanvas = document.getElementById('canvas');
    let dieline = dataCanvas.dataset.dieline;
    dieline = JSON.parse(dieline);

    const canvas = new fabric.Canvas('canvas', {
        selection: false // disable selection by default
    });

    // Wait for canvas to fully initialize
    setTimeout(() => {
        canvas.loadFromJSON(dieline, function() {
            canvas.renderAll();
            canvas.calcOffset(); // recalculate offsets
        });
    }, 100);
});

Solution 5: Updating Fabric.js

If you’re using an older version of Fabric.js, updating might solve the problem:

bash
npm install fabric@latest
# or
yarn add fabric@latest

As mentioned in issue #184, different versions have different callback handling, so updating to the latest version often solves compatibility issues.


Complete working example

Here’s a complete working example that solves the line display problem:

php
<?php
// PHP code for creating JSON
$dieline = [
    'objects' => [
        [
            'type' => 'line',
            'left' => 100,
            'top'  => 100,
            'x1'   => 0,
            'y1'   => 0,
            'x2'   => 200,
            'y2'   => 0,
            'stroke' => 'black',
            'strokeWidth' => 2,
            'selectable' => false,
            'evented' => false
        ]
    ],
    'background' => 'white'
];

// Passing to HTML
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Fabric.js Lines</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.0/fabric.min.js"></script>
</head>
<body>
    <canvas id="canvas" width="800" height="600" data-dieline='<?= json_encode($dieline) ?>'></canvas>
    
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const canvasElement = document.getElementById('canvas');
            const dieline = JSON.parse(canvasElement.dataset.dieline);
            
            const canvas = new fabric.Canvas('canvas', {
                selection: false,
                preserveObjectStacking: true
            });

            canvas.loadFromJSON(dieline)
                .then(() => {
                    canvas.renderAll();
                    console.log('Objects loaded successfully');
                    
                    // Force re-render
                    canvas.requestRenderAll();
                })
                .catch(error => {
                    console.error('Loading error:', error);
                });
        });
    </script>
</body>
</html>

Conclusion

The problem with displaying lines in Fabric.js when loading from JSON is usually solved by one of the following methods:

  1. Using Promise instead of callbacks - the most reliable solution for modern versions of Fabric.js
  2. Correctly setting coordinates - lines require either absolute coordinates or proper relative coordinate setup
  3. Additional initialization - sometimes explicit disabling of selection and events for lines is required
  4. Controlling loading order - ensure the canvas is fully initialized before loading data
  5. Updating the library - the latest version of Fabric.js often contains fixes for such issues

The most effective solution will be a combination of using Promise and correctly setting coordinates in the PHP code. This will ensure reliable line display during initial canvas loading.