Canvas自定义画散点图

备注:

显示的数据,代码中是从后端获取的,参考者可以自行造数据。

HTML

<div> 
    <div>
        <label style="margin-left: 80px" nz-checkbox [(ngModel)]="isHideOffset" (ngModelChange)="isHideChecked($event)">隐藏偏移量</label>
        <canvas #headCanvas width="800px;" height="50px"></canvas>
    </div>
    <div>
        <div >
            <canvas #LATCanvas id="LATCanvasId" width="2000px" height="130px" ></canvas>
        </div>
        <div>
            <canvas #LNTCanvas width="2000px" height="130px" id="LNTCanvasId"></canvas>
        </div>
        <div>
            <canvas #VRTCanvas width="2000px" height="130px" id="VRTCanvasId"></canvas>

        </div>

        <canvas #buttomCanvas width="2000px" height="130px"></canvas>
    </div>
</div>

TS

import { Component, OnInit, ElementRef, ViewChild, Input, OnChanges } from '@angular/core';
import { PV2BeamViewComponent } from '@app/uView/view-component/pv2-beam-view/pv2-beam-view.component';
import { THIS_EXPR } from '@angular/compiler/src/output/output_ast';
import * as moment from 'moment';
import { ImageBeamInfoServiceProxy, SetupOffsetRecordDto, ImageBeamTypes } from '@shared/service-proxies/service-proxies';

@Component({
    selector: 'offset-display',
    templateUrl: './offset-display.component.html',
    styleUrls: ['./offset-display.component.less']
})

export class OffsetDisplayComponent implements OnInit, OnChanges {
    ngOnChanges(changes: import("@angular/core").SimpleChanges): void {
        console.log("OffsetDisplayComponent:" + this.beamGroupId);
        this.constInterval = 25;
        this.displayModelList = [];
        this.displayModelList.push("Show All");
        this.displayModelList.push("Show LAT");
        this.displayModelList.push("Show LNG");
        this.displayModelList.push("Show VRT");
        this.selectedMode = "Show All";
        this.showAll = true;
        this.showLAT = true;
        this.showLNG = true;
        this.showVRT = true;
        this.isHideOffset = false;
        this.getOffsetSessions();

        this.initialHead();
    }

    @Input() beamGroupId: number;

    @ViewChild('LATCanvas') LATCanvas: ElementRef;

    @ViewChild('headCanvas') headCanvas: ElementRef;

    @ViewChild('LNTCanvas') LNTCanvas: ElementRef;

    @ViewChild('VRTCanvas') VRTCanvas: ElementRef;

    @ViewChild('buttomCanvas') buttomCanvas: ElementRef;


    public latcontext: any;

    public headContext: any;

    public lntContext: any;

    public vrtContext: any;

    public buttomContext: any;

    public constInterval: number;

    public offsetSessions: Array<OffsetPosition>;

    public displayDates: Array<string>;

    public dateMapX: Dictionary;

    public isHideOffset: boolean;

    public displayModelList: string[] = [];

    public selectedMode: string;

    public showAll: boolean;

    public showLAT: boolean = true;

    public showLNG: boolean;

    public showVRT: boolean;

    public fractionMapDate: Dictionary;

    public totalFx: string;

    public setupOffsetRecords: Array<SetupOffsetRecordDto>;

    public dates:Array<string>;



    ngOnInit(): void {


    }

    constructor(private _imageBeamService: ImageBeamInfoServiceProxy) {

    }

    displayModeChanged() {
        switch (this.selectedMode) {
            case "Show All":
                this.showAll = true;
                this.showLAT = true;
                this.showLNG = true;
                this.showVRT = true;
                break;
            case "Show LAT":
                this.showAll = false;
                this.showLAT = true;
                this.showLNG = false;
                this.showVRT = false;
                break;
            case "Show LNG":
                this.showAll = false;
                this.showLAT = false;
                this.showLNG = true;
                this.showVRT = false;
                break;
            case "Show VRT":
                this.showAll = false;
                this.showLAT = false;
                this.showLNG = false;
                this.showVRT = true;
                break;

            default:
                break;
        }
        this.initialOffsetDisplay();

    }

    private drawLine(context: any, type: TableType) {

        for (let index = 0; index <= this.dates.length; index++) {
            context.beginPath();
            context.strokeStyle = "white";
            context.setLineDash([]);
            context.moveTo(200 + index * 40, this.constInterval);
            context.lineTo(200 + index * 40, this.constInterval * 5)
            context.lineWidth = 0.3;
            context.stroke();
            context.save();
        }
        context.beginPath();
        context.strokeStyle = "white";
        context.lineWidth = 0.5;
        context.moveTo(200, this.constInterval);
        context.lineTo(200 + this.dates.length * 40, this.constInterval);
        context.moveTo(200, this.constInterval * 5);
        context.lineTo(200 + this.dates.length * 40, this.constInterval * 5);
        context.stroke();
        context.save();
        //画0刻度线
        context.beginPath();

        context.moveTo(200, this.constInterval * 3);
        context.lineTo(200 + this.dates.length * 40, this.constInterval * 3);
        context.strokeStyle = "white";
        context.lineWidth = 3;
        context.stroke();
        context.font = "10px 黑体";
        context.fillStyle = "white";
        context.textAlign = "right";
        context.textBaseline = "middle";
        context.fillText("0", 190, this.constInterval * 3);
        context.fillText("1", 190, this.constInterval * 2);
        context.fillText("-1", 190, this.constInterval * 4);
        context.font = "14px bold 黑体";
        context.textBaseline = "middle";
        switch (type) {
            case TableType.LAT:
                context.fillText("治疗床 LAT", 150, this.constInterval * 3);
                break;
            case TableType.LNG:
                context.fillText("治疗床 LNG", 150, this.constInterval * 3);
                break;
            case TableType.VRT:
                context.fillText("治疗床 VRT", 150, this.constInterval * 3);
                break;
            default:
                break;
        }
        context.save();
        //画虚线
        context.beginPath();
        context.setLineDash([5]);
        context.lineWidth = 0.5;
        context.moveTo(200, this.constInterval * 2);
        context.lineTo(200 + this.dates.length * 40, this.constInterval * 2);
        context.stroke();
        context.moveTo(200, this.constInterval * 4);
        context.lineTo(200 + this.dates.length * 40, this.constInterval * 4);
        context.stroke();
        context.save();

    }

    private setIfShow(element: any, isShow: boolean) {
        if (element != null) {
            if (isShow == false) {
                element.style.display = "none";
            }
            else {
                element.style.display = "block"
            }

        }
    }


    private initialOffsetDisplay() {

        this.latcontext = (<HTMLCanvasElement>this.LATCanvas.nativeElement).getContext('2d');
        this.latcontext.clearRect(0, 0, 2000, 200);

        this.lntContext = (<HTMLCanvasElement>this.LNTCanvas.nativeElement).getContext('2d');
        this.lntContext.clearRect(0, 0, 2000, 200);

        this.vrtContext = (<HTMLCanvasElement>this.VRTCanvas.nativeElement).getContext('2d');
        this.vrtContext.clearRect(0, 0, 2000, 200);

        let latelement = document.getElementById("LATCanvasId");
        this.setIfShow(latelement, this.showLAT);


        let lntelement = document.getElementById("LNTCanvasId");
        this.setIfShow(lntelement, this.showLNG);

        let vrtelement = document.getElementById("VRTCanvasId");
        this.setIfShow(vrtelement, this.showVRT);

        this.buttomContext = (<HTMLCanvasElement>this.buttomCanvas.nativeElement).getContext('2d');
        this.buttomContext.clearRect(0, 0, 2000, 200);

        
        this.initDates();

        for (let index = 0; index <= this.dates.length; index++) {
            this.buttomContext.beginPath();
            this.buttomContext.lineWidth = 0.3;
            this.buttomContext.strokeStyle = "white";
            this.buttomContext.setLineDash([]);
            this.buttomContext.moveTo(200 + index * 40, this.constInterval * 1);
            this.buttomContext.lineTo(200 + index * 40, this.constInterval * 2)
            this.buttomContext.stroke();
            this.buttomContext.save();
        }

        this.drawLine(this.latcontext, TableType.LAT);

        this.drawLine(this.lntContext, TableType.LNG);

        this.drawLine(this.vrtContext, TableType.VRT);

         //IGRT Date
         this.buttomContext.beginPath();
         this.buttomContext.strokeStyle = "white";
         this.buttomContext.setLineDash([]);
         this.buttomContext.lineWidth = 0.5;
         this.buttomContext.moveTo(200, this.constInterval );
         this.buttomContext.lineTo(200 + this.dates.length * 40, this.constInterval );
         this.buttomContext.moveTo(200, this.constInterval * 2);
         this.buttomContext.lineTo(200 + this.dates.length * 40, this.constInterval * 2);
         this.buttomContext.stroke();
         this.buttomContext.font = "14px 黑体";
         this.buttomContext.textBaseline = "middle";
         this.buttomContext.fillStyle = "white";
         this.buttomContext.textAlign = "right";
         this.buttomContext.fillText("IGRT 成像日期(MM-dd)", 150, this.constInterval * 1.5);
         this.buttomContext.save();

        //画坐标点
        this.initOffsetPosition();




    }

    private initDates() {
        this.displayDates = new Array<string>();
        this.dateMapX = new Dictionary();
        this.fractionMapDate = new Dictionary();
        var maxFxIndex = this.offsetSessions[0].FractionNumber;
        this.dates = new Array<string>();
        this.offsetSessions.forEach(element => {
            var day = moment(element.PlanDate).format('MM-DD');
            if(this.dates.indexOf(day) == -1){
                this.dates.push(day);
            }
        });
        for (let index = 0; index < this.dates.length; index++) {
            this.buttomContext.beginPath();
            this.buttomContext.lineWidth = 0.5;
            this.buttomContext.font = "10px 黑体";
            this.buttomContext.fillStyle = "white";
            this.buttomContext.textBaseline = "middle";
            this.buttomContext.fillText(this.dates[index], 210 + index * 40, this.constInterval * 1.5);
            this.buttomContext.save();
            this.displayDates.push(this.dates[index]);
            this.dateMapX.set(this.dates[index], 220 + index * 40);
        }
    }

    formatDate(time: any) {
        const Dates = new Date(time);
        const year: number = Dates.getFullYear();
        const month: any = (Dates.getMonth() + 1) < 10 ? '0' + (Dates.getMonth() + 1) : (Dates.getMonth() + 1);
        const day: any = Dates.getDate() < 10 ? '0' + Dates.getDate() : Dates.getDate();
        return month + '-' + day;

    }

    private initialHead() {
        this.headContext = (<HTMLCanvasElement>this.headCanvas.nativeElement).getContext('2d');
        this.headContext.clearRect(0, 0, 800, 50);
        //画圆形
        this.headContext.beginPath();
        this.headContext.arc(300, 30, 5, 0, Math.PI * 2, true);
        this.headContext.closePath();
        this.headContext.fillStyle = 'green';
        this.headContext.fill();
        this.headContext.save();
        this.headContext.beginPath();
        this.headContext.fillStyle = 'white';
        this.headContext.strokeStyle = "white";
        this.headContext.font = "14px bold 黑体";
        this.headContext.textBaseline = "middle";
        this.headContext.fillText("FBCT", 315, 30);
        this.headContext.save();

        //画矩形
        this.headContext.beginPath();
        this.headContext.fillStyle = 'green';
        this.headContext.fillRect(400, 25, 10, 10);
        this.headContext.fillStyle = 'green';
        this.headContext.save();
        this.headContext.beginPath();
        this.headContext.fillStyle = 'white';
        this.headContext.strokeStyle = "white";
        this.headContext.font = "14px bold 黑体";
        this.headContext.textBaseline = "middle";
        this.headContext.fillText("PV", 415, 30);
        this.headContext.save();

        //画三角形
        this.headContext.beginPath();
        this.headContext.fillStyle = 'green';
        var height = 10 * Math.sin(Math.PI / 3);//计算等边三角形的高
        this.headContext.moveTo(505, 25); //从A(345,25)开始
        this.headContext.lineTo(500, 25 + height);//从A(345,25)开始,画到B (340,25+height)结束
        this.headContext.lineTo(510, 25 + height); //B(340,25+height)-C(430,25+height)
        this.headContext.fill();
        this.headContext.save();
        this.headContext.beginPath();
        this.headContext.fillStyle = 'white';
        this.headContext.strokeStyle = "white";
        this.headContext.font = "14px bold 黑体";
        this.headContext.textBaseline = "middle";
        this.headContext.fillText("CBCT", 515, 30);
        this.headContext.save();


    }

    private initOffsetPosition() {
        if (this.offsetSessions != null) {
            this.offsetSessions.forEach(point => {
                switch (point.SessionType.id) {
                    case SessionType.FBCT:
                        this.drawFbctPosition(point);
                        break;
                    case SessionType.CBCT:
                        this.drawCbctPosition(point);
                        break;
                    case SessionType.PV:
                        this.drawPvPosition(point);
                        break;
                    default:
                        break;
                }
            });

        }

    }

    private drawFbctPosition(point: OffsetPosition) {
        var x = this.dateMapX.get(moment(point.PlanDate).format('MM-DD'));
        //1像素代表多少值
        var per = 2 / (2 * this.constInterval);
        //LAT
        if(point.LAT > 1){
           var xheight = (point.LAT - 1)/per;
           var y = this.constInterval*2 -xheight;
           this.drawPoint(SessionType.FBCT, x, y, this.latcontext);
           this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);//Lat label

        }
        else{
            var xheight = point.LAT / per;
            var y = this.constInterval * 3 - xheight;
            this.drawPoint(SessionType.FBCT, x, y, this.latcontext);
            this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);//Lat label
        }
      



        //LNT
        if(point.LNT > 1){
            var yheight = (point.LNT - 1)/per;
            var yLNT = this.constInterval*2 -yheight;
            this.drawPoint(SessionType.FBCT, x, yLNT, this.lntContext);
            this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT label
        }
        else{
            var yheight = point.LNT / per;
            var yLNT = this.constInterval * 3 - yheight;
            this.drawPoint(SessionType.FBCT, x, yLNT, this.lntContext);
            this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT label
        }




        //VRT
        if(point.VRT > 1){
            var zheight = (point.VRT-1) / per;
            var zVRT = this.constInterval*2  - zheight;
            this.drawPoint(SessionType.FBCT, x, zVRT, this.vrtContext);
            //VRT标坐标
            this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext);
        }
        else{
            var zheight = point.VRT / per;
            var zVRT = this.constInterval * 3 - zheight;
            this.drawPoint(SessionType.FBCT, x, zVRT, this.vrtContext);
            //VRT标坐标
            this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext);
        }



    }


    private drawCbctPosition(point: OffsetPosition) {
        var x = this.dateMapX.get(moment(point.PlanDate).format('MM-DD')) - 5;
        //1像素代表多少值
        var per = 2 / (2 * this.constInterval);
        //LAT
        if(point.LAT > 1){
            var xheight = (point.LAT - 1)/per;
            var y = this.constInterval*2 -xheight;
            this.drawPoint(SessionType.CBCT, x, y, this.latcontext);
            this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext); //LAT标坐标

        }
        else{
            var xheight = point.LAT / per;
            var y = this.constInterval * 3 - xheight;
            this.drawPoint(SessionType.CBCT, x, y, this.latcontext);
            this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext); //LAT标坐标
        }

        //LNT
        if(point.LNT > 1){
            var yheight = (point.LNT - 1)/per;
            var yLNT = this.constInterval*2 -yheight;
            this.drawPoint(SessionType.CBCT, x, yLNT, this.lntContext);
            this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
        }
        else{
            var yheight = point.LNT / per;
            var yLNT = this.constInterval * 3 - yheight;
            this.drawPoint(SessionType.CBCT, x, yLNT, this.lntContext);
            this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
        }



        //VRT
        if(point.VRT > 1){
            var zHeight = (point.VRT- 1 )/per;
            var zVRT = this.constInterval*2 - zHeight;
            this.drawPoint(SessionType.CBCT, x, zVRT, this.vrtContext);
            this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标
        }
        else{
            var zHeight = point.VRT /per;
            var zVRT = this.constInterval * 3 - zHeight;
            this.drawPoint(SessionType.CBCT, x, zVRT, this.vrtContext);
            this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标
        }

    }

    private drawPvPosition(point: OffsetPosition) {
        var x = this.dateMapX.get(moment(point.PlanDate).format('MM-DD')) - 5;
        //1像素代表多少值
        //LAT标坐标
        var per = 2 / (2 * this.constInterval);
        
        if(point.LAT > 1){
            var height = (point.LAT -1)/per;
            var y = this.constInterval - height;
            this.drawPoint(SessionType.PV, x - 5, y - 5, this.latcontext);
            this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);

        }
        else{
           var height = point.LAT / per;
           var y = this.constInterval * 3 - height;
           this.drawPoint(SessionType.PV, x - 5, y - 5, this.latcontext);
           this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);
        }
        



        //LNT
        if(point.LNT > 1){
            var height = (point.LNT - 1)/per;
            var yLNT = this.constInterval  - height;
            this.drawPoint(SessionType.PV, x, yLNT, this.lntContext);
            this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标

        }
        else{
            var yLNT = this.constInterval * 3 - height;
            this.drawPoint(SessionType.PV, x, yLNT, this.lntContext);
            this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
        }



        //VRT
        if(point.VRT > 1){
            var height = (point.VRT - 1)/per;
            var zVRT = this.constInterval  - height;
            this.drawPoint(SessionType.PV, x, zVRT, this.vrtContext);
            this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标

        }
        else{
            var zVRT = this.constInterval * 3 - height;
            this.drawPoint(SessionType.PV, x, zVRT, this.vrtContext);
            this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标

        }
       


    }

    private drawPositionLabel(label: number, x: number, y: number, context: any) {
        if (this.isHideOffset == false || this.isHideOffset == undefined) {
            context.beginPath();
            context.fillStyle = 'white';
            context.strokeStyle = "white";
            context.font = "10px bold 黑体";
            context.textBaseline = "middle";
            context.fillText(Number(label).toFixed(2), x, y);
            context.save();
        }
        else {

        }

    }

    private drawPoint(sessionType: SessionType, x: number, y: number, context: any) {
        switch (sessionType) {
            case SessionType.FBCT:
                context.beginPath();
                context.arc(x, y, 5, 0, Math.PI * 2, true);
                context.closePath();
                context.fillStyle = 'green';
                context.fill();
                context.save();
                break;
            case SessionType.CBCT:
                context.beginPath();
                var trangleheight = 10 * Math.sin(Math.PI / 3);//计算等边三角形的高
                context.moveTo(x, y - trangleheight / 2); //从A顶点开始
                context.lineTo(x - 5, y + trangleheight / 2);//从A开始,画到B 左下角结束
                context.lineTo(x + 5, y + trangleheight / 2); //B-C右下角
                context.fillStyle = 'green';
                context.fill();
                context.save();
                break;
            case SessionType.PV:
                context.beginPath();
                context.fillStyle = 'green';
                context.fillRect(x, y, 10, 10);
                context.save();

                break;
            default:
                break;
        }
    }

    private getOffsetSessions() {
        if (this.beamGroupId == null || this.beamGroupId == 0) {
            console.log("get offset session:beamgroup is " + this.beamGroupId);
            return;

        }
        console.log("get setupOffsetRecord:" + this.beamGroupId);
        //数据可以自己造
        this._imageBeamService.getSetupOffsetRecords(this.beamGroupId).subscribe(
            (result: Array<SetupOffsetRecordDto>) => {
                this.setupOffsetRecords = result;
                this.offsetSessions = new Array<OffsetPosition>();
                if (this.setupOffsetRecords != null) {
                    var index = 1;
                    this.setupOffsetRecords.forEach(record => {
                        var p = new OffsetPosition();
                        p.LAT = record.xOffset;
                        p.LNT = record.yOffset;
                        p.VRT = record.zOffset;
                        p.FractionNumber = index;
                        p.PlanDate = record.applyDateTime;
                        p.SessionType = record.imageBeamType;
                        this.offsetSessions.push(p);
                        index++;
                    });
                    this.initialOffsetDisplay();
                }
            });
    }

    getRandomNumInt(min: number, max: number) {
        var Range = max - min;
        var Rand = Math.random();
        return (min + Math.round(Rand * Range));
    }

    public isHideChecked(event: boolean) {
        if (event == undefined) {
            this.isHideOffset = false;

        }
        this.isHideOffset = event;
        this.initialOffsetDisplay();
    }
}

class OffsetPosition {
    public LAT: number;
    public LNT: number;
    public VRT: number;
    public SessionType: ImageBeamTypes;
    public PlanDate: moment.Moment | undefined;;
    public FractionNumber: number;
}

export class Dictionary {
    items: object;
    constructor() {
        this.items = {};
    }
    has(key: any): boolean {
        return this.items.hasOwnProperty(key);
    }
    set(key: any, val: any) {
        this.items[key] = val;
    }
    delete(key: any): boolean {
        if (this.has(key)) {
            delete this.items[key];
        }
        return false;
    }
    get(key: any): any {
        return this.has(key) ? this.items[key] : undefined;
    }
    values(): any[] {
        let values: any[] = [];
        for (let k in this.items) {
            if (this.has(k)) {
                values.push(this.items[k]);
            }
        }
        return values;
    }
}

enum SessionType {
    FBCT = 2,
    PV = 3,
    CBCT = 1,
}

enum TableType {
    LAT = 0,
    LNG = 1,
    VRT = 2,
}

效果图

在这里插入图片描述

有疑问请留言,因为没有精简代码,不是demo

发布了78 篇原创文章 · 获赞 0 · 访问量 1269

猜你喜欢

转载自blog.csdn.net/qq_21209307/article/details/105288472