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

时间:2015-10-28 12:35:58

标签: javascript html5 canvas fabricjs

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

我仍在努力解决尝试动态添加/删除路径的问题(我选择路径元素,因为我发现它是使其工作的最佳选择,但也适用于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);

http://jsfiddle.net/17ueLva2/17/

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

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

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

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

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

此外,一旦我可以添加/删除路径段而不破坏图形,我会尝试在用户拖放图形时动态更新。

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

任何形式的帮助都将受到赞赏。

1 个答案:

答案 0 :(得分:1)

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

Javascript能够实时显示充满数据的屏幕。因为只有一个屏幕可以看到一次有6个实时视图的信息似乎是多余的。

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

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

我希望我已经明白你要做的事情。如果不理睬我的答案。