/** * 产程图包括上下两部分,通过两个不同的svg来展示。 */ class PartoGram { constructor(args){ //上半部分Svg this.topSvg = args.topSvg; //下半部分Svg this.bottomSvg = args.bottomSvg; //svg左边距 this.marginLeft = args.marginLeft; //svg上边距 this.marginTop = args.marginTop; //svg右边距 this.marginRight = args.marginRight||40; //svg下边距 this.marginButtom = args.marginButtom||40; //d3.domain定义域 X轴 this.Domain_X = args.Domain_X||[0,24]; //上部svg的左边y坐标轴定义域 this.domain_TYL = args.domain_TYL||[0, 10]; //上部svg的右边y坐标轴定义域 this.domain_TYR = args.domain_TYR||[-5, 5]; //下部svg的右边y坐标轴定义域 this.domain_BYL = args.domain_BYL|| ['时间','胎心','血压','宫缩','处理','签名','评分','小结','备注','']; this.domain_scale =[0,1,2,3,4,9,11,12,15,16,17]; this.yCutOut = this.yCutOut||11 //请求后台数据地址 this.url = args.url; this._scaleTopSvgX = null;//x轴的坐标标尺 this._scaleTopSvgLeftY = null//topSvg左边的Y轴坐标尺 this._scaleTopSvgRightY = null//topSvg右边的Y轴坐标尺 this._scaleBottomSvgY = null;//bottomSvgY轴坐标尺 this._scaleBottomSvgX = null;//bottomSvgX轴坐标尺 this.run(); } run(){ this.initTopSvgXaxris(); this.initTopSvgY1axris(); this.initTopSvgY2axris(); //下部svg this.initBottomSvgAxris(); } initTopSvgXaxris(){ //横坐标的最大长度 let topsvg = d3.select("#"+this.topSvg.id); let xScaleMax = topsvg.attr("width")-this.marginRight let xScale = d3.scaleLinear() //输入范围 .domain(this.Domain_X) //输出范围 .range([this.marginLeft, xScaleMax]); this._scaleTopSvgX=xScale;//设置topSvgX轴坐标尺 //纵坐标网格线长度 let xAxisLength = (topsvg.attr("height")-this.marginButtom-this.marginTop) let xAxis = d3.axisBottom(xScale) .ticks(this.Domain_X[this.Domain_X.length-1]-this.Domain_X[0]).tickSize(-xAxisLength); //横坐标在Y方向的偏移量 let yOffest= topsvg.attr("height")-this.marginButtom; topsvg.append("g").call(xAxis) .call(this.tasf,0,yOffest); } initTopSvgY1axris(){ let topsvg = d3.select("#"+this.topSvg.id); var range_y = topsvg.attr("height") - this.marginButtom-this.marginTop; let yScale1 = d3.scaleLinear() .domain(this.domain_TYL) .range([topsvg.attr("height") - this.marginButtom, this.marginTop]); this._scaleTopSvgLeftY = yScale1; let yAxisLength = (topsvg.attr("width")-this.marginLeft-this.marginRight) let yAxis1 = d3.axisLeft().scale(yScale1).tickSize(-yAxisLength); topsvg.append("g") .call(yAxis1) .call(this.tasR,90) .call(this.tasf,this.marginLeft,0); topsvg.append("g").call(this.textMkr,"宫颈扩张(厘米)红色",this.marginLeft-30,this.marginTop+(range_y/2),"label_lr"); topsvg.append("g").call(this.textMkr,"胎头下降(厘米)蓝色",topsvg.attr("width")-10,this.marginTop+(range_y/2),"label_lr"); } initTopSvgY2axris(){ let topsvg = d3.select("#"+this.topSvg.id); let yScale2 = d3.scaleLinear() .domain(this.domain_TYR) .range([topsvg.attr("height")- this.marginButtom, this.marginTop]); this._scaleTopSvgRightY = yScale2; let yAxis2 = d3.axisRight(yScale2).tickSize(0); topsvg.append("g") .call(yAxis2) .call(this.tasf,topsvg.attr("width")-this.marginRight,0); } //初始化下边的svg的y坐标轴 initBottomSvgAxris(){ let bottomsvg = d3.select("#"+this.bottomSvg.id); let yMax = this.domain_scale[this.domain_scale.length-1]; let ySourceDomainL = [0,yMax]; let yTargetDomain = [0,bottomsvg.attr("height")-this.marginButtom]; let y2Scale = d3.scaleLinear() .domain(ySourceDomainL) .range(yTargetDomain); this._scaleBottomSvgY = y2Scale; let yAxisLength = (bottomsvg.attr("width")-this.marginLeft-this.marginRight) let y2Axis = d3.axisLeft(y2Scale).tickSize(-yAxisLength).tickValues(this.domain_scale) .tickFormat("");//自定义刻度; bottomsvg.append("g") .call(y2Axis) .call(this.tasf,this.marginLeft,0) //横坐标的最大长度 let xScaleMax = bottomsvg.attr("width")-this.marginRight; let xScale = d3.scaleLinear() //输入范围 .domain(this.Domain_X) //输出范围 .range([this.marginLeft, xScaleMax]); this._scaleBottomSvgX = xScale; //纵坐标网格线长度 let xAxisLength = (bottomsvg.attr("height")-this.marginButtom) let xAxis = d3.axisTop(xScale) .ticks(this.Domain_X[this.Domain_X.length-1]-this.Domain_X[0]).tickSize(-xAxisLength*this.yCutOut/yMax).tickSizeOuter(-xAxisLength); //横坐标在Y方向的偏移量 bottomsvg.append("g").call(xAxis); //画最后一行的竖线 var x_range = this.Domain_X[this.Domain_X.length-1] var df = Math.round(x_range/3);// var ds =[[df,yMax-1],[df,yMax]]; this.lineMkr(xScale,y2Scale,bottomsvg,ds); ds =[[df*2,yMax-1],[df*2,yMax]]; this.lineMkr(xScale,y2Scale,bottomsvg,ds); var yMax_range = bottomsvg.attr("height")-this.marginButtom; let minimum_sc = yMax_range/yMax bottomsvg.append("g").call(this.textMkr,"时间",this.marginLeft-20,8+minimum_sc-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"胎心",this.marginLeft-20,8+minimum_sc*2-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"血压",this.marginLeft-20,8+minimum_sc*3-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"宫缩",this.marginLeft-20,8+minimum_sc*4-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"处",this.marginLeft-20,8+minimum_sc*5-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"理",this.marginLeft-20,8+minimum_sc*7-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"签",this.marginLeft-20,8+minimum_sc*10-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"名",this.marginLeft-20,8+minimum_sc*11-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"评分",this.marginLeft-20,8+minimum_sc*12-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"小结",this.marginLeft-20,8+minimum_sc*14-minimum_sc/2,"label_tb"); bottomsvg.append("g").call(this.textMkr,"备注",this.marginLeft-20,8+minimum_sc*16-minimum_sc/2,"label_tb"); var ys_xy =[[0,yMax]]; ys_xy = ys_xy.map(d => [ xScale(d[0]), y2Scale(d[1]) ] ); bottomsvg.append("g") .selectAll("#ys") .data(ys_xy).enter() .call(this.textTransMkr,"医生:",d => d[0],d => d[1]-12); var jcz_xy =[[df,yMax]] jcz_xy =jcz_xy.map(d => [ xScale(d[0]), y2Scale(d[1]) ] ); bottomsvg.append("g") .selectAll("#jcz") .data(jcz_xy).enter() .call(this.textTransMkr,"接产者:",d => d[0],d => d[1]-12); var xh_xy =[[df*2,yMax]] xh_xy =xh_xy.map(d => [ xScale(d[0]), y2Scale(d[1]) ] ); bottomsvg.append("g") .selectAll("#xhys") .data(xh_xy).enter() .call(this.textTransMkr,"巡回:",d => d[0],d => d[1]-12); //画斜线,血压 var domain_X = this.Domain_X; var domain_Y = this.domain_scale; let bp_Y1 = domain_Y[3]; let bp_Y2 = domain_Y[2]; for(let i =domain_X[0];i[ xScale(d[0]), yScale(d[1])]); let lnMkr = d3.line(); sel.append("g") .append("path") .attr("d",lnMkr(ds)) .attr("fill","none") .attr("stroke","red"); } textMkr(sel,label,x,y,style){ let label_ = d3.local(),dsX=d3.local(),dsY =d3.local(); sel.each(function(d ,i){ label_.set(this,typeof label==="function"?label(d,i):label||0) }); sel.each(function(d ,i){ dsX.set(this,typeof x==="function"?x(d,i):x||0) }); sel.each(function(d ,i){ dsY.set(this,typeof y==="function"?y(d,i):y||0) }); sel.append("text") .attr("x",function(){return dsX.get(this)}||0) .attr("y",function(){return dsY.get(this)}||5) .classed(style||"label_tb",true) .text(function(){return label_.get(this)}); } textTransMkr(sel,label,x,y){ sel.append("text") .attr("x",x||0) .attr("y",y||5) .attr("transform","translate(5,-2)") .text(label?label:d=>d); } //transform的公用方法 tasf(sel,dx,dy){ var dxs=d3.local(), dys=d3.local(); sel.each(function(d ,i){ dxs.set(this,typeof dx==="function"?dx(d,i):dx||0) }); sel.each(function(d,i){ dys.set(this,typeof dy==="function"?dy(d,i):dy||0) }); return sel.attr("transform",function(){ return "translate("+dxs.get(this)+","+dys.get(this)+")" }) } //旋转角度 tasR(sel,r){ var dr = d3.local(); sel.each(function(d ,i){ dr.set(this,typeof r==="function"?r(d,i):r||0) }); return sel.attr("transform",function(){ return "rotate("+dr.get(this)+")" }) } //对外接口-----begin setBottomTextStr(ds,x,y,xOffset=0.1,yOffest=-0.1,style){ let xSc = this._scaleBottomSvgX ;//bottomSvgX轴坐标尺 let ySc = this._scaleBottomSvgY ;//bottomSvgY轴坐标尺 let svg = d3.select("#"+this.bottomSvg.id); svg.append("g") .append("text") .classed(style,true) .attr("x",xSc(x+xOffset)) .attr("y",ySc(y+yOffest)) .text(ds); } //设置普通文本,通过svg来画 setText(id,ds,x,y,style){ let svg = d3.select("#"+id); svg.append("g") .append("text") .classed(style,true) .attr("x",x) .attr("y",y) .text(ds); } // /** * 宫颈扩张 * @param {ds格式必须是[[0,1],[1,2]]二维数组} ds * @param {点和线的颜色} color * @param {点的半径} r */ setDilatationCervix(ds,color,r){ let len = ds.length; var resDs = [] for(let i =0;i [ xSc(d[0]), ySc(d[1]) ] ); let topsvg = d3.select("#"+this.topSvg.id); topsvg.append("g") .selectAll("circle") .data(resDs).enter() .append("circle").attr("r",r||4) .attr("cx",d =>d[0]).attr("cy",d => d[1]) .style("fill",color||"blue") // .style("stroke", "red"); // 8 var lnMaker = d3.line(); topsvg.append("g").append("path") .attr("d",lnMaker(resDs)) .attr("fill","none") .attr("stroke",color||"blue"); } /** * 胎头下降 @param {ds格式必须是[[0,1],[1,2]]二维数组} ds * @param {点和线的颜色} color * @param {点的半径} r */ setFetalHeadDescent(ds,color,r){ let len = ds.length; var resDs = [] for(let i =0;i [ xSc(d[0]), ySc(d[1]) ] ); let topsvg = d3.select("#"+this.topSvg.id); topsvg.append("g") .selectAll("circle") .data(resDs).enter() .append("circle").attr("r",r||4) .attr("cx",d =>d[0]).attr("cy",d => d[1]) .style("fill",color||"red") // .style("stroke", "red"); // 8 var lnMaker = d3.line(); topsvg.append("g").append("path") .attr("d",lnMaker(resDs)) .attr("fill","none") .attr("stroke",color||"red") .attr("stroke-dasharray","5,5") } /** * * @param {数据来源,按一定顺序的一维数组} ds * @param {在y轴的那个地方显示} y_axis * @param {偏差} offset * @param {文本样式} style */ setTextData (ds,y_axis,xOffset,yOffset,style){ //把一维数组转化为二维数组 let len = ds.length; var resDs = [] for(let i =0;i[scaleX(d[0]),scaleY(d[1]),d[2]]) bottomsvg.append("g") .selectAll("#data") .data(scaleXy).enter() .append("text") .attr("x",d =>d[0]) .attr("y",d =>d[1]) .classed(style,true) .text(d =>d[2]); } }