繁体   English   中英

Fabric.js 动态添加/删除元素

[英]Fabric.js dynamically add/remove elements

在查看了几个库以查看哪个库更适合我需要做的事情之后,我开始使用 Fabric.js 库,最后选择了 Fabric.js(也尝试了 Paper.js 或 Chars.js 或 Raphael.js)。

我仍在努力解决尝试向路径动态添加/删除段的问题(我选择了 Path 元素,因为我发现它是使其工作的最佳选择,但也适用于 Polyline 元素),但这似乎是不可能的去做吧。

这是代码的一部分:

function getRandomInt(min, max) {
    return Math.floor(Math.random() * (max - min + 1) + min);
}

//Seteo un array para todos los canvas que va a tener el grafico
var canvas= Array();

//Seteo un array para lineas guias que va a tener el grafico
var guias= Array();

//Seteo un array con todos los colores posibles
var color=Array();

//Seteo un array para todas las variables / lineas que va a tener el grafico
var graficos= Array();

var polyline= Array();

//Seteo un array para todas las variables / puntos que va a tener el grafico
var points= Array();

//Capturo todos los elementos canvas de la clase especifica
var Ocanvas;

//Capturo el evento de Drag&Drop
var isDragging=false;
var dragPoints={x:0,y:0};

//Se generan random un cantidad X de colores (cantidad= variable total)
for(var c=0;c <= 500; c++){
    var rojo=getRandomInt(0,155);
    var azul=getRandomInt(0,155);
    var verde=getRandomInt(0,155);
    transparencia=getRandomInt(5,10)/10;
    color[c]="rgba("+rojo+","+verde+","+azul+","+transparencia+")";
}

//Seteo el total de elementos que deseo mostrar en cada canvas (siempre tomar en cuenta que comienza desde 0 a contar)
var total=3;

//Seteo el nro de elemento inicial de cada Path
var iPath=0;

</script>
<script type="text/javascript">
jQuery(document).ready(function(){
    //Capturo todos los elementos canvas de la clase especifica
    Ocanvas=jQuery("canvas.canvas-basic");

    //Obtengo el ancho de las columnas de canvas
    var OriginalWidth=parseInt(Ocanvas.innerWidth());

    //Obtengo el alto de las columnas de canvas
    var OriginalHeight=parseInt(Ocanvas.innerHeight());

    var actual_zoom=0;

    //Defino el valor del ancho de las lineas de los graficos
    var Vstroke=2.5;

    //var canvas_color = color[getRandomInt(0,500)];
    var canvas_color="#E6E6E6";

    jQuery.each(Ocanvas, function(i, v){

        var aux_id = jQuery(this).attr("id");

        canvas[i] = new fabric.Canvas(aux_id, {
            containerClass:"canvas-conteiner",
            backgroundColor: canvas_color,
            renderOnAddRemove:false,
            width: OriginalWidth,
            height: OriginalHeight,
            selection:false,
            stateful: false,
        });

        //Creo una linea con 2 pixeles mas de largo para que 
        guias[i] = new fabric.Line([0,-1, OriginalWidth+2, -1],{
            top:OriginalHeight,
            left:-1, //muevo la linea a la posicion -1 para evitar que queden espacios entre los canvas
            strokeWidth:0.5,
            stroke: "#f00",
            lockMovementY: true,
            lockRotation: true,
            selectable: false,
            //stroke: color[getRandomInt(0,500)],
        });

        canvas[i].add(guias[i]);

        canvas[i].labelGuia=new labelGuia({
            backgroundColor: color[getRandomInt(0,500)],
        });

        canvas[i].add(canvas[i].labelGuia);
    });

    canvas.renderAll();

    //Agrego funcionamiento al evento de movimiento del mouse
    jQuery("body").delegate(".upper-canvas.canvas-basic", "mousemove", event, function(obj){

        if(isDragging){
            jQuery(this).addClass("cursor-move");
        }

        var strokeColor=color[getRandomInt(0,500)];

        //alert(strokeColor);

        //var index= jQuery(this).parents(".col-canvas").attr("data-index");

        if(canvas.getZoom()>1){
            obj.offsetY= (obj.offsetY / canvas.getZoom());
        }

        lastmove=0;

        lastpositionY=obj.offsetY+lastmove;
        var valY=lastpositionY;

        guias.set({
            opacity:1,
            top:valY,
            stroke:"#000",
        }); 

        guias.bringToFront();
        
        canvas.renderAll();
    });

    jQuery("body").delegate(".upper-canvas.canvas-basic", "mousedown", event, function(e){
        isDragging=true;
        dragPoints.x = e.offsetX;
        dragPoints.y = e.offsetY;
        jQuery(this).css("cursor","move");
    });

    jQuery("body").delegate(".upper-canvas.canvas-basic", "mouseup", event, function(e){
        if( dragPoints.x != e.offsetX && dragPoints.y != e.offsetY){
            //console.log("isDragging x0:"+ dragPoints.x+" y0:"+ dragPoints.y + " / x1:"+e.offsetX+" y1:"+e.offsetY);
            var posX=graficos.get("top");
            var moveX=posX + (e.offsetY-dragPoints.y);
            graficos.set({
                top: moveX,
            });

            canvas.renderAll();
        }
        jQuery(this).removeClass("cursor-move");
        isDragging=false;
    });

    //Agrego funcionamiento al evento de la "rueda" del mouse
    jQuery(".upper-canvas.canvas-basic").on("mousewheel", function (e) {
        var strokeWidthWheel=1;
        
        var delta = (e.originalEvent.wheelDelta / 1200);
        var pointer=e;

        var actual_points={
            x:e.offsetX, 
            y:e.offsetY,
        };

        var movePoints={ 
            x:e.offsetX,
            y:e.offsetY+50
        }

        lastpositionY=pointer.offsetY / canvas.getZoom();

        var valY=lastpositionY;

        delta=Math.round(delta * 100) /100; //Esto esta hecho asi para que el valor quede con 2 decimales nada mas.

        actual_zoom= Math.round( canvas.getZoom() * 100) /100;
        
        if(delta < 0 && (actual_zoom + delta) > 1){
            //canvas.zoomToPoint(actual_points, actual_zoom+delta);
            //canvas.setZoom(actual_zoom+delta);
            canvas.zoomToPoint(movePoints, actual_zoom+delta);
            graficos.setScaleY(actual_zoom+delta);
            //graficos.setOriginX(actual_points.x);
            strokeWidthWheel= guias.get("strokeWidth")+delta;
        }else if(delta > 0){
            //canvas.setZoom(actual_zoom+delta);
            canvas.zoomToPoint(movePoints, actual_zoom+delta);
            graficos.setScaleY(actual_zoom+delta);
            //graficos.setOriginX(actual_points.x);
            strokeWidthWheel= guias.get("strokeWidth")-delta;
        }           

        if(strokeWidthWheel<0.5){
            strokeWidthWheel=0.5;
        }

        guias.set({
            opacity:1,
            top:valY,
            strokeWidth: strokeWidthWheel,
        });

        //console.log(canvas[0]);

        if((actual_zoom + delta) == 1){
            canvas.zoomToPoint({x:0,y:0}, 1);
            graficos.set({top:-1});
            guias.set({
                left:-1
            });
            canvas.setCoords();
            canvas.fxCenterObjectV();
        }

        canvas.renderAll();
    });

    var xCount=0;

    //Genero graficos de forma aleatoria 
    jQuery.each(canvas, function(i, v){
        for(var j=0; j< total; j++){
            var pos_j=j%total;
            var posX=getRandomInt(50,65)*(pos_j+1);
            var posY=0;             

            graficos[xCount] = new fabric.Path("M "+posX+",-1 L "+posX+","+posY
                , {
                    strokeWidth: Vstroke,
                    stroke: color[getRandomInt(0,500)],
                    originX: 'left',
                    //originY: 'top',
                    left:posX,
                    top:0,
                    opacity:1,
                    hasBorders:false,
                    backgroundColor: "transparent",
                    fill: "transparent",
                    lockScalingX:true,
                }
            );

            graficos[xCount].posicionY=posY;

            graficos[xCount].posicionX=posX;

            points[xCount]=Array();

            canvas[i].add(graficos[xCount]);

            //Beta Test use Polyline instead of Path
            polyline[xCount] = new fabric.Polyline( [ {x: posX, y: posY } ]
                , {
                    strokeWidth: Vstroke,
                    stroke: color[getRandomInt(0,500)],
                    originX: 'left',
                    //originY: 'top',
                    left:posX,
                    top:0,
                    opacity:1,
                    hasBorders:false,
                    backgroundColor: "transparent",
                    fill: "transparent",
                    lockScalingX:true,
                }
            );

            canvas[i].add(polyline[xCount]);

            xCount++;
        }
    });

});
var itemToRemove=0;

var oldPath=Array();

function cachePath(init, max, ar){
    if(ar instanceof Array){
        var len=ar.length;
        if(len>0){
            if(init < max){

            }else{
                return false;   
            }
        }else{
            return false;
        }
    }else{
        return false;
    }
}

var CONST_escale=25;

window.setInterval(function(){
    
    for(var j=0; j < graficos.length; j++){
        graficos[j].posicionY=graficos[j].get("minY")+iPath*CONST_escale;
        //graficos[j].path[iPath]=Array("L", graficos[j].posicionX, graficos[j].posicionY);
        polyline[j].points[iPath]={x:graficos[j].posicionX, y:graficos[j].posicionY};
        //graficos[j].posicionY = graficos[j].posicionY + 1;
        //points[j][iPath]={ x: graficos[j].posicionX, y: graficos[j].get("minY")+iPath};

        //console.log("posY:"+parseInt(graficos[j].posicionY) +" - Height: "+ parseInt(canvas.getHeight()));

        if( parseInt(graficos[j].posicionY) > parseInt(canvas.getHeight()) ){
            //graficos[j].setTop(graficos[j].get("top") - 1);
            graficos[j].setTop(graficos[j].get("top") - CONST_escale);
            if(iPath>=10){
                //graficos[j].set({stroke:color[getRandomInt(0,500) ] } );
                //polyline[j].points.splice(iPath-10,1);
                polyline[j].points[iPath-10]=null;
                //polyline[j].points[iPath].set({stroke:color[getRandomInt(0,500) ] } );
                //points[j].splice(itemToRemove,1);
                //itemToRemove++;
                //console.log("points["+j+"]:"+points[j].length);
                //graficos[j].path.shift();
                //graficos[j].path[0][0]="M";
                //console.log(graficos[j].path[0][0]);
            }
        }

        if(iPath==100*itemToRemove){
            //var oldPath[j][itemToRemove]=graficos[j].path.splice(100, graficos[j].path.length-100);
            //graficos[j].path=graficos[j].path.splice(itemToRemove,100);
            //itemToRemove++;
            //console.log("points["+j+"]:"+points[j].length);
            //graficos[j].path.shift();
            //graficos[j].path[0][0]="M";
            //console.log(graficos[j].path[0][0]);
            itemToRemove++;
        }

        if(canvas[j] != undefined ){
            //console.log(canvas[j].labelGuia);
            canvas[j].labelGuia.updateText("label: "+ iPath * CONST_escale * j);
        }
    }
    
    if( (iPath % 1) == 0 ){
        for(var j=0; j < graficos.length; j++){
            var rand=getRandomInt(-65,65);
            var pos_j=j%total;
            var pos_X=65*(pos_j+1) + rand;
            graficos[j].posicionX = pos_X;
            var pos_Y=graficos[j].get("minY")+iPath;

            graficos[j].path[iPath]=Array("L", pos_X, pos_Y*CONST_escale);

            points[j][iPath]=Array();
            points[j][iPath][0]=graficos[j].posicionX;
            points[j][iPath][1]=graficos[j].get("minY")+iPath;
        }
    } 

    iPath++;

    canvas.renderAll();
},500);

https://jsfiddle.net/17ueLva2/17/

我有 4 个 Canvas 元素(将来可以使其更加动态,用户可以添加/删除 Canvas),我需要在其中绘制 1 到 4 条线段(所有段将具有相同的高度,换句话说x 值对所有人都相同)。 在这个例子中,为了最简单,我在每条线中画了 3 条线。

我还需要我动态绘制的路径会不断增长(某些情况下可能会持续绘制 4 或 6 个小时,并且每 1/2 秒将向每个路径添加 1 个元素)所以我想删除一些旧的它们从可视化中消失后的路径(假设我有一个高度为 300 像素的画布,如果该段的高度为 500 像素,那么我想删除每个路径的第一个像素并使路径高度始终保持在 500 像素)。

我尝试使用 Javascript 函数shiftsplice 在每种情况下,在删除 Fabrics Path 的路径数组中的元素后,代码中断或图形甚至消失。

我需要知道我正在尝试做的事情是否可行,或者是否有更多经验的人可以在这方面给我一些帮助。

此外,系统在不停止的情况下绘制的时间可能在 2 小时到 10 小时之间,因此删除“旧”路径非常重要。

此外,一旦我可以在不破坏图形的情况下添加/删除路径段,我将尝试在用户拖放图形时进行动态更新。

这是针对我正在开发的实时系统。

任何形式的帮助将不胜感激。

我看过你的小提琴。 我不完全确定您想要什么,因为我看不懂西班牙语,但我不得不说您打算以错误的方式保存数据。 画布不是存储数据的地方,而是用于查看数据的窗口。 您将数据存储在适当的数组或数据存储中,并根据需要呈现该数据的视图。

Javascript 不仅能够实时显示充满数据的屏幕。 由于一次只能看到一个充满信息的屏幕,因此 6 个实时取景似乎是多余的。

将您正在记录的数据存储到一个数组中。 创建一个渲染函数,将数据渲染到画布上,只有当画布在视图中并且只有适合画布的部分数据时(如果你想看到最后一小时,只渲染最后一小时,而不是整个样本集,就像您现在所做的那样),并且只能以可以渲染的分辨率(没有点在 200 像素宽的视图上渲染 20000 个样本。而是获取每 100 个样本的平均值并仅渲染 200 个平均值,或者也获得最小值, max 如果需要准确的可视化。)

向您展示代码的工作量太大,但我能想到的最接近您应该考虑和调查的事情是用于查看音频和音乐的波形查看器。 它们旨在显示来自非常大的数据集(数百万个样本中的数十个)的样本,并且可以实时执行此操作。 他们通过渲染数据的视图(部分)来实现这一点,而不是通过渲染所有数据然后缩放和平移到您想要看到的内容。

我希望我已经理解你想要做什么。 如果不是忽略我的回答。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM