Ich habe vor ein paar Tagen eine Diagrammbibliothek verwendet, von der Baidus ECharts die beste zu sein scheint. Canvas-Diagramme sind bei der Verarbeitung großer Datenmengen besser. Dann werde ich auch Canvas verwenden, um eine Diagrammbibliothek zu implementieren. Es fühlt sich nicht allzu schwierig an. Lassen Sie uns zuerst ein einfaches Balkendiagramm implementieren.
Der Effekt ist wie folgt: Zu den Hauptfunktionspunkten gehören:Werfen wir zunächst einen Blick auf die Verwendung einiger ECharts-Verwendungsmethoden. Zuerst übergeben wir das HTML-Tag, um das Diagramm anzuzeigen, rufen dann init auf und übergeben die Daten während der Initialisierung.
var con=document.getElementById('container'); var chart=new Bar(con); chart.init({ title:'Ganzjähriges Niederschlagshistogramm', xAxis:{// x-Achsen-Daten: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'] }, yAxis:{//y-axis name:'water volume', formatter:'{value} ml' }, series:[//Group data{ name:'Niederschlag im Osten', Daten:[62,20,17,45,100,56,19,38,50,120,56,130] }, { name:'Niederschlag im Westen', Daten:[52,10,17 ,25 ,60,39,19,48,70,30,56,8] }, { name:'Südlicher Niederschlag', data:[12,10,17,25,27,39,50,38,100,30,56,90] }, { color:'hsla(270,80%,60%,1)', name:'Nördlicher Niederschlag Menge', Daten:[12,30,17,25,7,39,49,38,60,30,56,10] } ] });
Für die Diagrammbasisklasse werden wir später auch Kreisdiagramme und Liniendiagramme schreiben, sodass wir die gemeinsamen Teile extrahieren. Beachten Sie, dass „canvas.style.width“ und „canvas.width“ unterschiedlich sind. Ersteres streckt die Grafiken, während letzteres das ist, was wir normalerweise verwenden und die Grafiken nicht streckt. Der Zweck, hier zuerst die Erweiterung und dann die Reduzierung zu schreiben, besteht darin, das Problem der Unschärfe beim Zeichnen von Text auf Leinwand zu lösen.
class Chart{ constructionor(container){ this.container=container; this.canvas=document.createElement('canvas'); this.ctx=this.canvas.getContext('2d'); this.H=600*2; this.padding=120; this.paddingTop=50; this.series=[]; //Lösen Sie das Problem der Schriftunschärfe, indem Sie die Größe verdoppeln this.canvas.width=this.W; this.canvas.height=this.W/ 2 + 'px'; this.canvas.style.height = this.H/2 + 'px';
Um das Histogramm zu initialisieren, rufen Sie Object.assign(this,opt) in es6 auf. Dies entspricht der Extend-Methode in JQ, die die Eigenschaften in die aktuelle Instanz kopiert. Gleichzeitig wird auch ein Tip-Attribut erstellt, das ein HTML-Tag ist und zur späteren Anzeige von Dateninformationen verwendet wird. Zeichnen Sie dann die Grafiken und binden Sie Mausereignisse.
class Bar erweitert Chart{ constructionor(container){ this.xAxis={}; this.yAxis=[] } init(opt){ Object.assign(this,opt) ; if(!this.container)return; this.container.style.position='relative'; this.tip.cssText='display: none; opacity: 0.5; border-radius: 5px; : 99;'; this.container.appendChild(this.canvas); this.container.appendChild(this.tip); this.draw(); this.bindEvent(); } draw(){//Zeichnung} showInfo(){//Anzeigen von Informationen} animate(){//Animation durchführen} showData(){//Anzeigen von Daten}
Zeichnen Sie die XY-Achse
Zeichnen Sie zuerst den Titel, dann die XY-Achse, durchlaufen Sie dann die gruppierte Datenreihe, die komplexe Berechnungen enthält, zeichnen Sie dann den Maßstab der XY-Achse, zeichnen Sie die Gruppenbeschriftung und zeichnen Sie schließlich die Daten. Bei der Datenelementreihe handelt es sich um gruppierte Daten, die eins zu eins den xAxis.data der X-Achse entsprechen. Jedes Element kann einen benutzerdefinierten Namen und eine benutzerdefinierte Farbe haben. Wenn nicht angegeben, wird der Name nunamed zugewiesen und die Farbe wird automatisch generiert. Das Legendenattribut wird hier auch zum Aufzeichnen der Tag-Listeninformationen verwendet, da es bei nachfolgenden Mausklicks nützlich ist, um festzustellen, ob der Klick korrekt ist.
Wichtigste Wissenspunkte von Canvas:
draw(){ var that=this, ctx=this.ctx, canvas=this.canvas, W=this.W, H=this.H, padding=this.padding, paddingTop=this.paddingTop, xl=0,xs=0,xdis=W-padding*2,//Anzahl der x-Achsen-Einheiten, Länge jeder Einheit, Gesamtlänge der x-Achse yl=0,ys=0,ydis=H-padding* 2-paddingTop; //Die Anzahl der Y-Achsen-Einheiten, die Länge jeder Einheit, die Gesamtlänge der Y-Achse ctx.fillStyle='hsla(0,0%,20%,1)'; ctx.strokeStyle='hsla(0,0%,10%,1)'; ctx.textAlign='center'; ctx.font='24px arial'; ctx.clearRect(0,0,W,H); if(this.title){ ctx.save(); ctx.textAlign='left'; ctx.font='bold 40px arial'; { ctx.fillText(this.yAxis.name,padding,padding+paddingTop-30 } // x-Achse ctx.save(); ctx.translate(padding,H-padding(0,0); ctx .Stroke(); // Skalierung der x-Achse if(this.xAxis&&(xl=this.xAxis.data.length)){ xs=(W-2*padding)/xl; this.xAxis.data.forEach((obj,i)=>{ var x=xs*(i+1); ctx.moveTo(x,0); ctx. lineTo(x,10); ctx.fillText(obj,x-xs/2,40 }); ctx.restore(); // y-Achse ctx.save(); ctx.StrokeStyle='hsl(220,100%,50%)'; . moveTo(0,0); ctx.lineTo(0,2*padding+paddingTop-H); ctx.Stroke(); ctx.restore(); if(this.series.length){ var curr,txt,dim,info,item,tw=0; ;i++){ item=this.series[i]; if(!item.data||!item.data.length){ this.series.splice(i--,1);continue; Elemente ohne Farbe zuweisen if(!item.color){ var hsl=i%2?180+20*i/2:20*(i-1); item.color='hsla('+hsl+',70% , 60%,1)'; } item.name=item.name||'unnamed'; // Gruppierungsbezeichnungen zeichnen ctx.save(); ctx.translate(padding+W/4,paddingTop+40); that.legend.push({ hide:item.hide||false, name:item.name, color:item.color, x:padding+that.W /4+i*90+tw, y:paddingTop+40, w:60, h:30, r:5 }); ctx.fillStyle=item.color; ctx.StrokeStyle=item.color; RoundRect(ctx,i*90+tw,0,60,30,5); ctx.globalAlpha=item.hide?0.3:1; (); ctx.fillText(item.name,i*90+tw+70,26); tw+=ctx.measureText(item.name).width;//Berechnen Sie die Zeichenlänge ctx.restore(); if(item.hide)continue; //Berechnen Sie die Daten auf der Y-Achsen-Skala if(!info){ info=calculateY( item.data.slice(0,xl)); } curr=calculateY(item.data.slice(0,xl)); if(curr.max>info.max){ info=curr; } if(!info) return; ys=ydis/yl; //Y-Achsen-Skala zeichnen ctx.fillStyle='hsl(200,100%,60% ) '; ctx.translate(padding,H-padding); for(var i=0;i<=yl;i++){ ctx.beginPath(); ctx.StrokeStyle='hsl(220,100%,50%)'; ctx.moveTo(-10,-Math.floor(ys*i)); ctx.lineTo(0,-Math.floor(ys*i)); ctx.Stroke(); ctx.beginPath(); ctx.StrokeStyle='hsla(0,0%,80%,1)'; ctx.moveTo(0,-Math.floor(ys*i)); ctx.lineTo(xdis,-Math.floor(ys*i)); ctx.textAlign='right'; Math.min(Math.floor(info.step*i),info.max); txt=this.yAxis.formatter?this.yAxis.formatter.replace('{value}',dim):dim ctx.fillText(txt,-20,-ys*i+10); ; //Daten zeichnen this.showData(xl,xs,info.max);Plotdaten
Da das Datenelement anschließend animiert und angezeigt werden muss, wenn die Maus darüber fährt, wird es in die Animationswarteschlange animateArr gestellt. Hier müssen wir die gruppierten Daten erweitern, die beiden vorherigen verschachtelten Arrays in eine Ebene konvertieren und die Attribute jedes Datenelements berechnen, z. B. Name, X-Koordinate, Y-Koordinate, Breite, Geschwindigkeit und Farbe. Nachdem die Daten organisiert sind, wird die Animation ausgeführt.
showData(xl,xs,max){ //Daten zeichnen var that=this, ctx=this.ctx, ydis=this.H-this.padding*2-this.paddingTop, sl=this.series.filter(s= >!s.hide).length, sp=Math.max(Math.pow(10-sl,2)/3-4,5), w=(xs-sp*(sl+1))/sl, h,x,index=0; that.animateArr.length=0; // Erweitern Sie die Datenelemente und füllen Sie die Animationswarteschlange aus for(var i=0 ,item ,len=this.series.length;i<len;i++){ item=this.series[i]; item.data.slice(0,xl).forEach((d,j)=>{ h=d/max*ydis; x=xs*j+w*index+sp*(index+1); that.animateArr .push({ index:i, name:item.name, num:d, x:Math.round(x), y:1, w:Math.round(w), h:Math.floor(h+ 2) , vy:Math.max(300,Math.floor(h*2))/100, color:item.color });Animation ausführen
Zur Ausführung der Animation gibt es nicht viel zu sagen, es handelt sich lediglich um eine selbstausführende Abschlussfunktion. Das Prinzip der Animation besteht darin, den Geschwindigkeitswert vy sequentiell auf der y-Achse zu akkumulieren. Denken Sie jedoch daran, dass die Warteschlange, wenn sie mit der Ausführung der Animation fertig ist, gestoppt werden muss. Daher gibt es ein isStop-Flag, das jedes Mal beurteilt wird, wenn die Warteschlange mit der Ausführung fertig ist.
animate(){ var that=this, ctx=this.ctx, isStop=true; (function run(){ isStop=true; for(var i=0,item;i<that.animateArr.length;i++){ item =that.animateArr[i]; if(item.y-item.h>=0.1){ item.y=item.h } else { item.y+=item.vy; } if(item.y<item.h){ ctx.save(); // ctx.translate(that.padding+item.x,that.H-that.padding); ctx.fillStyle=item.color; .fillRect(that.padding+item.x,that.H-that.padding-item.y,item.w,item.y); isStop=false; } } if(isStop)return; requestAnimationFrame(run }())}Bindendes Ereignis
Ereignis 1: Überprüfen Sie beim Bewegen der Maus, ob sich die Mausposition auf der Gruppenbeschriftung oder dem Datenelement befindet. Rufen Sie nach dem Zeichnen des Pfads isPointInPath(x,y) auf, wenn dies der Fall ist B. ein Datenelement, müssen Sie auch die Spalte neu zeichnen, die Transparenz festlegen und sie unterscheiden. Der Inhalt muss auch angezeigt werden. Dies ist ein Div, das relativ zum übergeordneten Container-Container positioniert ist. Das Tip-Attribut wurde während der Initialisierung festgelegt. Wir kapseln den Anzeigeteil in die showInfo-Methode.
Ereignis 2: Wenn Mousedown auftritt, bestimmen Sie, auf welche Gruppenbezeichnung die Maus klickt, und legen Sie dann das Ausblenden-Attribut in der entsprechenden Gruppendatenreihe fest. Wenn es wahr ist, bedeutet dies, dass das Element nicht angezeigt wird, und rufen Sie dann die Zeichenmethode auf , überschreiben Sie das Rendern und Zeichnen und führen Sie eine Animation durch.
bindEvent(){ var that=this, canvas=this.canvas, ctx=this.ctx; this.canvas.addEventListener('mousemove',function(e){ var isLegend=false; // pos=WindowToCanvas(canvas,e .clientX,e.clientY); var box=canvas.getBoundingClientRect(); var pos = { x:e.clientX-box.left, y:e.clientY-box.top }; // Gruppierungsbezeichnung for(var i=0,item,len=that.legend.length;i<len;i++){ item =that.legend[i]; ctx.save(); RoundRect(ctx,item.x,item.y,item.w,item.h,item.r); Da es verdoppelt wird, sind die Koordinaten *2 if(ctx.isPointInPath(pos.x*2,pos.y*2)){ canvas.style.cursor='pointer'; isLegend=true ; break; } canvas.style.cursor='default'; ctx.restore(); } if(isLegend) return; i=0,item,len=that.animateArr.length;i<len;i++){ item=that.animateArr[i]; ctx.rect(that.padding+item.x,that.H-that.padding-item.h,item.w,item.h); if(ctx.isPointInPath(pos.x*2,pos.y*2)){ //Löschen Sie die Grafiken und zeichnen Sie sie dann mit einer Transparenz von 0,5 neu. ctx.clearRect(that.padding+item.x,that.H-that . padding-item.h,item.w,item.h); ctx.globalAlpha=0.5; that.showInfo(pos,item); break } canvas.style.cursor='default'; ctx.globalAlpha=1 ); ctx.restore(); },false); this.canvas.addEventListener('mousedown',function(e){ e.preventDefault(); var box=canvas.getBoundingClientRect(); var pos = { x:e.clientX-box.left, y:e.clientY-box.top }; =that.legend.length;i<len;i++){ item=that.legend[i]; RoundRect(ctx,item.x,item.y,item.w,item.h,item.r); // Da es verdoppelt wird, sind die Koordinaten *2 if(ctx.isPointInPath(pos.x*2, pos .y*2)){ that.series[i].hide=!that.series[i].hide; that.animateArr.length=0; } },false); } //Daten anzeigen showInfo(pos,obj){ var txt=this.yAxis.formatter?this.yAxis.formatter.replace('{value}',obj.num):obj.num; box=this.canvas.getBoundingClientRect(); var con=this.container.getBoundingClientRect(); this.tip.innerHTML = '<p>'+obj.name+':'+txt+'</p>'; this.tip.style.left=(pos.x+(box.left-con.left)+10 )+'px'; this.tip.style.top=(pos.y+(box.top-con.top)+10)+'px';Zusammenfassen
Was hier abgeschlossen ist, ist nur ein grundlegender Effekt. Tatsächlich gibt es viele Bereiche, die weiter optimiert werden müssen, wie z. B. reaktionsfähige Unterstützung, mobile Unterstützung, Animationseffekte, Unterstützung für mehrere Y-Achsen, Anzeigeinhaltseffekte und Unterstützung für Polylinienfunktionen .
Das Obige ist der gesamte Inhalt dieses Artikels. Ich hoffe, dass er für das Studium aller hilfreich ist. Ich hoffe auch, dass jeder das VeVb Wulin Network unterstützt.