Fabricjs — рендеринг нескольких холстов в одном документе

#javascript #html #fabricjs

#javascript #HTML #fabricjs

Вопрос:

У меня возникла проблема с динамическим созданием и настройкой фонов холста. Я хочу, чтобы пользователь мог комментировать изображения текстом и фигурами, вот почему я делаю это таким образом.

Я хотел бы знать, почему этот код выдает такой результат

Идея

  1. Отправьте запрос get на конечную точку, которая возвращает данные json, содержащие URL-адреса изображений.
  2. Преобразуйте эти данные в массив javascript.
  3. Динамически создавайте холсты fabric js на основе длины вышеуказанного массива.
  4. Установите фоны холста для изображений, используя их URL-адреса. (т.е. У каждого холста будет другой фон, взятый из URL-адреса)

Проблема

Фоновое изображение есть только у последнего холста.

код

 <!DOCTYPE html>
<html>
  <head>
    <style>
      body {
      }
    </style>
    <script
      src="https://code.jquery.com/jquery-3.5.1.min.js"
      integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
      crossorigin="anonymous"
    ></script>
  </head>
  <body>
    <script src="fabric.js"></script>
    <!--new start-->
    <script>
      //procedurally load images from a given source
      //endpoint with image links
      var end_point = "http://localhost:8080/endpoint.php";
      var settings = {
        url: "http://localhost:8080/endpoint.php",
        method: "GET",
      };
      //get the images array from end point
      $.ajax(settings).done(function (images_json) {
        var images = JSON.parse(images_json);
        //procedurally create farbric.js canvas for each image element in the html document and set their background as the corresponding image.
        //for each item in the images array, create a fabric.js canvas with its background set as the image itself
        var canvas_array = [];
        for (var i = 0, len = images.length; i < len; i  ) {
          //console.log(images[i]);
          //create canvas and then make it a fabric canvas
          document.body.innerHTML  = `<canvas 
          id=${[i]} 
          width="1000" 
          height="200"
          style="border-style: solid;">
          </canvas>`;
          //canvases stored in canvas_array
          canvas_array[i] = new fabric.Canvas(`${[i]}`);
          console.log(canvas_array[i]);
          //set canvas background as the image
          canvas_array[i].setBackgroundImage(
            `${images[i]}`,
            canvas_array[i].renderAll.bind(canvas_array[i]),
            {
              backgroundImageOpacity: 1,
              backgroundImageStretch: false,
            }
          );
        }
      });
    </script>
    <!--new end-->
  </body>
</html>  

Результат

Результат

обратите внимание, что код генерирует правильное количество холстов. Однако фоновое изображение, похоже, не работает

Желаемый результат будет иметь 10 холстов с разным фоном, соответствующим URL-адресам изображений.

Я новичок в fabric.js , это может быть глупой ошибкой.

Ответ №1:

Использование удаленного источника было строгим требованием. Я попробовал несколько вещей, и это, наконец, сработало.

код

 //c global variable
var n_can_arr = [];
var f_can_arr = [];
var img_arr = [];

//c procedurally load images from a given source
$.ajax({
  url: "http://localhost:8080/endpoint.php",
  type: "GET",
  dataType: "json", //c added data type
  success: function (res) {
    for (var index = 0; index < res.length; index  ) {
      //c create canvas
      var setHtml = `<canvas
        id=${index} 
        width="1000"
        height="800"
        style="border-style: solid;">
        </canvas>`;
      document.body.innerHTML  = setHtml;
      //c update canvas and image arrays
      n_can_arr.push(index);
      img_arr.push(res[index]);
    }
    //c call image set after the loop is over
    $(document).trigger("images_set");
  },
});

//c on image_set called
$(document).bind("images_set", () => {
  //c for each element of normal canvas array, create a fabric js canvas and set its background
  for (var i = 0; i < n_can_arr.length; i  ) {
    create_canvas(i);
  }
  //c for each element of fabric canvas array, apply the extend canvas function
  extend_canvas();
});

function create_canvas(i) {
  //c create fabric js canvases with normal canvas id from canvas arrray
  var canvas = new fabric.Canvas(`${i}`);
  f_can_arr.push(canvas);

  //c set canvas background using image array
  canvas.setBackgroundImage(img_arr[i], canvas.renderAll.bind(canvas), {
    backgroundImageOpacity: 1,
    backgroundImageStretch: false,
  });
}

function extend_canvas() {
  f_can_arr.forEach((canvas) => {
    var origX, origY, isDown, mode_rect, mode_uline, mode_free, pointer;

    //c setting keypress listener
    $(window).on("keypress", (e) => {
      console.log(e.key);
      //c drawing rectangles
      if (e.key == 1) {
        var rect;
        console.log("Box");
        isDown = true;
        mode_free = false;
        mode_uline = false;
        mode_rect = true;

        //c canvas event listners
        canvas.on("mouse:down", function (o) {
          isDown = true;
          if (mode_rect) {
            pointer = canvas.getPointer(o.e);
            origX = pointer.x;
            origY = pointer.y;
            console.log(origX   ","   origY);
            rect = new fabric.Rect({
              left: origX,
              top: origY,
              originX: "left",
              originY: "top",
              width: pointer.x - origX,
              height: pointer.y - origY,
              fill: "red",
              angle: 0,
              fill: "rgba(255,0,0,0.0)",
              stroke: "black",
              strokeWidth: 1,
            });
            canvas.add(rect);
          }
        });

        canvas.on("mouse:move", function (o) {
          if (mode_rect) {
            if (isDown) {
              var pointer = canvas.getPointer(o.e);

              if (origX > pointer.x) {
                rect.set({ left: Math.abs(pointer.x) });
              }
              if (origY > pointer.y) {
                rect.set({ top: Math.abs(pointer.y) });
              }

              rect.set({ width: Math.abs(origX - pointer.x) });
              rect.set({ height: Math.abs(origY - pointer.y) });

              canvas.renderAll();
            }
          }
        });
      }

      //c freehand drawing/Highlighter
      if (e.key == 2) {
        console.log("freehand");
        isDown = true;
        mode_free = true;
        mode_uline = false;
        mode_rect = false;
        canvas.isDrawingMode = 1;
        canvas.freeDrawingBrush.color = "rgba(255,0,0,0.2)";
        canvas.freeDrawingBrush.width = 20;

        //c canvas event listners
        canvas.on("mouse:down", function (o) {
          isDown = true;
          if (mode_free) {
            canvas.renderAll();
          }
        });
      }

      //c line mode
      if (e.key == 3) {
        var line;
        console.log("line");
        isDown = true;
        mode_free = false;
        mode_uline = true;
        mode_rect = false;

        //c canvas event listners
        canvas.on("mouse:down", function (o) {
          isDown = true;
          var pointer = canvas.getPointer(o.e);
          var points = [pointer.x, pointer.y, pointer.x, pointer.y];
          if (mode_uline) {
            line = new fabric.Line(points, {
              strokeWidth: 3,
              fill: "red",
              stroke: "red",
              originX: "center",
              originY: "center",
              targetFindTolerance: true,
            });
            canvas.add(line);
          }
        });
        canvas.on("mouse:move", function (o) {
          if (!isDown) return;
          var pointer = canvas.getPointer(o.e);

          if (mode_uline) {
            line.set({ x2: pointer.x, y2: pointer.y });
            canvas.renderAll();
          }
        });
      }

      //c deleting a selected shape
      if (e.key == 4) {
        var activeObject = canvas.getActiveObject();
        if (activeObject) {
          canvas.remove(activeObject);
        }
      }

      //c cancling freehand drawing mode
      if (e.key == "x") {
        console.log("freehand mode cancled");
        canvas.isDrawingMode = 0;
      }
    });

    //c removing previous event listeners and resetting some global variables
    canvas.on("mouse:up", function (o) {
      isDown = false;
      mode_free = false;
      mode_uline = false;
      mode_rect = false;
      canvas.off("mouse:down");
      canvas.off("mouse:move");
    });
  });
}

function save_canvas() {}

function load_canvas() {}  

решение

Я настраиваю пользовательское событие запуска после выполнения вызова ajax. Только после запуска триггерного события я создаю холсты fabricjs. (по-видимому, даже с обещаниями код структуры выполнялся не в правильном порядке. Что, вероятно, было связано с плохим синтаксисом. Пользовательские триггеры решают эту проблему.)

Результаты

-Каждое изображение отображается отдельно в своем собственном fabric.js холст в качестве фона. -Каждый холст независим.

Ответ №2:

Создайте для вас просто скопируйте код, а затем используйте свои собственные изображения в local, чтобы получить фактический полный результат в выходном div (изображения будут сгенерированы).

       let CANVAS_LIST = [];
        let imageList = [
            'https://homepages.cae.wisc.edu/~ece533/images/airplane.png',
            'https://homepages.cae.wisc.edu/~ece533/images/arctichare.png',
            'https://homepages.cae.wisc.edu/~ece533/images/baboon.png',
        ]

        imageList.forEach(element => {


            let canvasElement = document.createElement("canvas");
            canvasElement.width = '300';
            canvasElement.height = '300';
            $("#canvas_container").append(canvasElement);
            let canvas = new fabric.Canvas(canvasElement);

            // Adding Example Text here. 
            canvas.add(new fabric.Text('This text is added', {
                fontFamily: 'Delicious_500',
                color: 'red',
                left: 10,
                top: 100
            }));
            // Setting up  Background to dynamic generated Canvas
            canvas.setBackgroundImage(
                `${element}`,
                canvas.renderAll.bind(canvas),
                {
                    backgroundImageOpacity: 1,
                    backgroundImageStretch: false,
                }
            );

            CANVAS_LIST.push(canvas);
        });

        setTimeout(() => {
            generateOutput();
        }, 3000);
        function generateOutput() {
            $("#output").empty();
            CANVAS_LIST.forEach(canvas => {
                let image = $("<img>").attr({
                    width: 200,
                    height: 200,
                    src: canvas.toDataURL()

                });

                image.css({ marginLeft: '20px' });

                $("#output").append(image);
            })
        }  
 <!DOCTYPE html>
<html>
<head>
    <style>
        body {}
    </style>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"
        integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/4.1.0/fabric.min.js"></script>
    <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css">
</head>

<body>
    <div class="container">
        <div id="canvas_container">
        </div>
        <button onclick="generateOutput()"> Show output</button>
        <div id="output">

        </div>
    </div>
   
</body>
</html>  

Спасибо

Комментарии:

1. привет, я получаю сообщение «Испорченные холсты не могут быть экспортированы» при вызове toDataURL. Также мне бы очень хотелось знать, почему мой код не работает в первую очередь. обновленный пост.

2. Не используйте удаленные изображения при работе с локальными, в приведенном выше примере есть удаленные URL-адреса, просто загрузите их в папку, а затем поместите в одну папку и запустите ее на сервере localhost. это будет работать.

3. это вам помогает?