diff --git a/dist/vue-bars.common.js b/dist/vue-bars.common.js index aa3e8d8..233590d 100644 --- a/dist/vue-bars.common.js +++ b/dist/vue-bars.common.js @@ -61,24 +61,43 @@ function genPoints (inArr, ref, ref$1) { var minY = ref.minY; var maxX = ref.maxX; var maxY = ref.maxY; + var minBarHeight = ref.minBarHeight; + var labelRotate = ref.labelRotate; var max = ref$1.max; var min = ref$1.min; var arr = inArr.map(function (item) { return (typeof item === 'number' ? item : item.value); }); - var minValue = Math.min.apply(Math, arr.concat( [min] )) - 0.001; + var minValue = Math.min.apply(Math, arr.concat( [min] )); + var maxValue = Math.max.apply(Math, arr.concat( [max] )); + var absMaxVal = Math.abs(maxValue); + var absMinVal = Math.abs(minValue); var gridX = (maxX - minX) / (arr.length - 1); - var gridY = (maxY - minY) / (Math.max.apply(Math, arr.concat( [max] )) + 0.001 - minValue); + var labelHeight = 20; + + var delta = 0; + if (minValue < 0 && maxValue < 0) { + delta = absMinVal; + } else if (minValue < 0 && maxValue >= 0) { + delta = absMinVal + absMaxVal; + } else if (minValue >= 0 && maxValue >= 0) { + delta = maxValue; + } + + var heightMultiplier = delta !== 0 ? (maxY - minY - labelHeight) / delta : 1; + var yAdjust = minValue * heightMultiplier < minBarHeight ? minBarHeight : 0; + var zeroLine = minValue < 0 ? absMinVal : 0; return arr.map(function (value, index) { - var title = typeof inArr[index] === 'number' ? inArr[index] : inArr[index].title; + var label = typeof inArr[index].title !== 'undefined' ? inArr[index].title : ''; + var title = typeof inArr[index].value === 'number' ? inArr[index].value : inArr[index]; + var height = Math.abs(value); + var barHeight = (height * heightMultiplier - yAdjust > minBarHeight ? height * heightMultiplier - yAdjust : minBarHeight); return { x: index * gridX + minX, - y: - maxY - - (value - minValue) * gridY + - +(index === arr.length - 1) * 0.00001 - - +(index === 0) * 0.00001, - v: title + y: maxY - barHeight - (value >= 0 || value === 0 && minValue >= 0 ? zeroLine * heightMultiplier : zeroLine * heightMultiplier - barHeight) - labelHeight - yAdjust, + height: barHeight, + label: label, + title: title } }) } @@ -87,6 +106,8 @@ function genBars (_this, arr, h) { var ref = _this.boundary; var maxX = ref.maxX; var maxY = ref.maxY; + var labelRotate = ref.labelRotate; + var labelColor = ref.labelColor; var totalWidth = (maxX) / (arr.length - 1); if (!_this.barWidth) { _this.barWidth = totalWidth - (_this.padding || 5); @@ -101,15 +122,15 @@ function genBars (_this, arr, h) { } var offsetX = (totalWidth - _this.barWidth) / 2; - return arr.map(function (item, index) { + var rects = arr.map(function (item, index) { return h('rect', { attrs: { id: ("bar-id-" + index), fill: (gradients ? gradients[index] : (_this.gradient[0] ? _this.gradient[0] : '#000')), x: item.x - offsetX, - y: 0, + y: item.y, width: _this.barWidth, - height: (maxY - item.y), + height: item.height, rx: _this.rounding, ry: _this.rounding } @@ -118,14 +139,53 @@ function genBars (_this, arr, h) { attrs: { attributeName: 'height', from: 0, - to: (maxY - item.y), + to: item.height, dur: ((_this.growDuration) + "s"), fill: 'freeze' } }), - h('title', {}, [item.v]) + h('title', {}, [item.title]) ]) - }) + }); + var translateOffsetX = labelRotate >= 0 ? 10 : -10; + var xaxis = h( + 'g', + { + attrs: { + class: 'x-axis', + transform: ("translate(" + translateOffsetX + "," + (maxY - 8) + ")") + } + }, + arr.map(function (item, index) { + var labelOffsetX = labelRotate < 0 ? item.x + offsetX : item.x - offsetX; + return h( + 'g', + { + attrs: { + class: 'v-bars--tick', + transform: ("translate(" + labelOffsetX + ",0) rotate(" + labelRotate + ")") + } + }, + [ + h( + 'text', + { + attrs: { + class: 'v-bars--label-text', + style: ("text-anchor:middle; fill:" + labelColor + ";"), + 'font-size': '0.7em', + title: item.title + } + }, + [ + item.label + ] + ) + ] + ) + }) + ); + return rects.concat(xaxis); } var Path = { @@ -140,11 +200,14 @@ var Path = { var points = genPoints(data, boundary, { max: max, min: min }); var bars = genBars(this, points, h); - return h('g', { - attrs: { - transform: ("scale(1,-1) translate(0,-" + (this.boundary.maxY) + ")") - } - }, bars) + return h( + 'g', + { + class: 'container', + transform: ("translate(0," + (this.boundary.maxY) + ")") + }, + bars + ) } }; @@ -177,6 +240,18 @@ var Bars = { type: Number, default: Infinity }, + minBarHeight: { + type: Number, + default: 3 + }, + labelRotate: { + type: Number, + default: -45 + }, + labelColor: { + type: String, + default: '#999999' + }, height: Number, width: Number, padding: { @@ -197,7 +272,10 @@ var Bars = { minX: padding, minY: padding, maxX: viewWidth - padding, - maxY: viewHeight - padding + maxY: viewHeight - padding, + minBarHeight: this.minBarHeight, + labelRotate: this.labelRotate, + labelColor: this.labelColor }; var props = this.$props; diff --git a/dist/vue-bars.esm.js b/dist/vue-bars.esm.js index a81f63d..7cfab6b 100644 --- a/dist/vue-bars.esm.js +++ b/dist/vue-bars.esm.js @@ -59,24 +59,43 @@ function genPoints (inArr, ref, ref$1) { var minY = ref.minY; var maxX = ref.maxX; var maxY = ref.maxY; + var minBarHeight = ref.minBarHeight; + var labelRotate = ref.labelRotate; var max = ref$1.max; var min = ref$1.min; var arr = inArr.map(function (item) { return (typeof item === 'number' ? item : item.value); }); - var minValue = Math.min.apply(Math, arr.concat( [min] )) - 0.001; + var minValue = Math.min.apply(Math, arr.concat( [min] )); + var maxValue = Math.max.apply(Math, arr.concat( [max] )); + var absMaxVal = Math.abs(maxValue); + var absMinVal = Math.abs(minValue); var gridX = (maxX - minX) / (arr.length - 1); - var gridY = (maxY - minY) / (Math.max.apply(Math, arr.concat( [max] )) + 0.001 - minValue); + var labelHeight = 20; + + var delta = 0; + if (minValue < 0 && maxValue < 0) { + delta = absMinVal; + } else if (minValue < 0 && maxValue >= 0) { + delta = absMinVal + absMaxVal; + } else if (minValue >= 0 && maxValue >= 0) { + delta = maxValue; + } + + var heightMultiplier = delta !== 0 ? (maxY - minY - labelHeight) / delta : 1; + var yAdjust = minValue * heightMultiplier < minBarHeight ? minBarHeight : 0; + var zeroLine = minValue < 0 ? absMinVal : 0; return arr.map(function (value, index) { - var title = typeof inArr[index] === 'number' ? inArr[index] : inArr[index].title; + var label = typeof inArr[index].title !== 'undefined' ? inArr[index].title : ''; + var title = typeof inArr[index].value === 'number' ? inArr[index].value : inArr[index]; + var height = Math.abs(value); + var barHeight = (height * heightMultiplier - yAdjust > minBarHeight ? height * heightMultiplier - yAdjust : minBarHeight); return { x: index * gridX + minX, - y: - maxY - - (value - minValue) * gridY + - +(index === arr.length - 1) * 0.00001 - - +(index === 0) * 0.00001, - v: title + y: maxY - barHeight - (value >= 0 || value === 0 && minValue >= 0 ? zeroLine * heightMultiplier : zeroLine * heightMultiplier - barHeight) - labelHeight - yAdjust, + height: barHeight, + label: label, + title: title } }) } @@ -85,6 +104,8 @@ function genBars (_this, arr, h) { var ref = _this.boundary; var maxX = ref.maxX; var maxY = ref.maxY; + var labelRotate = ref.labelRotate; + var labelColor = ref.labelColor; var totalWidth = (maxX) / (arr.length - 1); if (!_this.barWidth) { _this.barWidth = totalWidth - (_this.padding || 5); @@ -99,15 +120,15 @@ function genBars (_this, arr, h) { } var offsetX = (totalWidth - _this.barWidth) / 2; - return arr.map(function (item, index) { + var rects = arr.map(function (item, index) { return h('rect', { attrs: { id: ("bar-id-" + index), fill: (gradients ? gradients[index] : (_this.gradient[0] ? _this.gradient[0] : '#000')), x: item.x - offsetX, - y: 0, + y: item.y, width: _this.barWidth, - height: (maxY - item.y), + height: item.height, rx: _this.rounding, ry: _this.rounding } @@ -116,14 +137,53 @@ function genBars (_this, arr, h) { attrs: { attributeName: 'height', from: 0, - to: (maxY - item.y), + to: item.height, dur: ((_this.growDuration) + "s"), fill: 'freeze' } }), - h('title', {}, [item.v]) + h('title', {}, [item.title]) ]) - }) + }); + var translateOffsetX = labelRotate >= 0 ? 10 : -10; + var xaxis = h( + 'g', + { + attrs: { + class: 'x-axis', + transform: ("translate(" + translateOffsetX + "," + (maxY - 8) + ")") + } + }, + arr.map(function (item, index) { + var labelOffsetX = labelRotate < 0 ? item.x + offsetX : item.x - offsetX; + return h( + 'g', + { + attrs: { + class: 'v-bars--tick', + transform: ("translate(" + labelOffsetX + ",0) rotate(" + labelRotate + ")") + } + }, + [ + h( + 'text', + { + attrs: { + class: 'v-bars--label-text', + style: ("text-anchor:middle; fill:" + labelColor + ";"), + 'font-size': '0.7em', + title: item.title + } + }, + [ + item.label + ] + ) + ] + ) + }) + ); + return rects.concat(xaxis); } var Path = { @@ -138,11 +198,14 @@ var Path = { var points = genPoints(data, boundary, { max: max, min: min }); var bars = genBars(this, points, h); - return h('g', { - attrs: { - transform: ("scale(1,-1) translate(0,-" + (this.boundary.maxY) + ")") - } - }, bars) + return h( + 'g', + { + class: 'container', + transform: ("translate(0," + (this.boundary.maxY) + ")") + }, + bars + ) } }; @@ -175,6 +238,18 @@ var Bars = { type: Number, default: Infinity }, + minBarHeight: { + type: Number, + default: 3 + }, + labelRotate: { + type: Number, + default: -45 + }, + labelColor: { + type: String, + default: '#999999' + }, height: Number, width: Number, padding: { @@ -195,7 +270,10 @@ var Bars = { minX: padding, minY: padding, maxX: viewWidth - padding, - maxY: viewHeight - padding + maxY: viewHeight - padding, + minBarHeight: this.minBarHeight, + labelRotate: this.labelRotate, + labelColor: this.labelColor }; var props = this.$props; diff --git a/dist/vue-bars.js b/dist/vue-bars.js index ee73313..0c479d3 100644 --- a/dist/vue-bars.js +++ b/dist/vue-bars.js @@ -65,24 +65,43 @@ var minY = ref.minY; var maxX = ref.maxX; var maxY = ref.maxY; + var minBarHeight = ref.minBarHeight; + var labelRotate = ref.labelRotate; var max = ref$1.max; var min = ref$1.min; var arr = inArr.map(function (item) { return (typeof item === 'number' ? item : item.value); }); - var minValue = Math.min.apply(Math, arr.concat( [min] )) - 0.001; + var minValue = Math.min.apply(Math, arr.concat( [min] )); + var maxValue = Math.max.apply(Math, arr.concat( [max] )); + var absMaxVal = Math.abs(maxValue); + var absMinVal = Math.abs(minValue); var gridX = (maxX - minX) / (arr.length - 1); - var gridY = (maxY - minY) / (Math.max.apply(Math, arr.concat( [max] )) + 0.001 - minValue); + var labelHeight = 20; + + var delta = 0; + if (minValue < 0 && maxValue < 0) { + delta = absMinVal; + } else if (minValue < 0 && maxValue >= 0) { + delta = absMinVal + absMaxVal; + } else if (minValue >= 0 && maxValue >= 0) { + delta = maxValue; + } + + var heightMultiplier = delta !== 0 ? (maxY - minY - labelHeight) / delta : 1; + var yAdjust = minValue * heightMultiplier < minBarHeight ? minBarHeight : 0; + var zeroLine = minValue < 0 ? absMinVal : 0; return arr.map(function (value, index) { - var title = typeof inArr[index] === 'number' ? inArr[index] : inArr[index].title; + var label = typeof inArr[index].title !== 'undefined' ? inArr[index].title : ''; + var title = typeof inArr[index].value === 'number' ? inArr[index].value : inArr[index]; + var height = Math.abs(value); + var barHeight = (height * heightMultiplier - yAdjust > minBarHeight ? height * heightMultiplier - yAdjust : minBarHeight); return { x: index * gridX + minX, - y: - maxY - - (value - minValue) * gridY + - +(index === arr.length - 1) * 0.00001 - - +(index === 0) * 0.00001, - v: title + y: maxY - barHeight - (value >= 0 || value === 0 && minValue >= 0 ? zeroLine * heightMultiplier : zeroLine * heightMultiplier - barHeight) - labelHeight - yAdjust, + height: barHeight, + label: label, + title: title } }) } @@ -91,6 +110,8 @@ var ref = _this.boundary; var maxX = ref.maxX; var maxY = ref.maxY; + var labelRotate = ref.labelRotate; + var labelColor = ref.labelColor; var totalWidth = (maxX) / (arr.length - 1); if (!_this.barWidth) { _this.barWidth = totalWidth - (_this.padding || 5); @@ -105,15 +126,15 @@ } var offsetX = (totalWidth - _this.barWidth) / 2; - return arr.map(function (item, index) { + var rects = arr.map(function (item, index) { return h('rect', { attrs: { id: ("bar-id-" + index), fill: (gradients ? gradients[index] : (_this.gradient[0] ? _this.gradient[0] : '#000')), x: item.x - offsetX, - y: 0, + y: item.y, width: _this.barWidth, - height: (maxY - item.y), + height: item.height, rx: _this.rounding, ry: _this.rounding } @@ -122,14 +143,53 @@ attrs: { attributeName: 'height', from: 0, - to: (maxY - item.y), + to: item.height, dur: ((_this.growDuration) + "s"), fill: 'freeze' } }), - h('title', {}, [item.v]) + h('title', {}, [item.title]) ]) - }) + }); + var translateOffsetX = labelRotate >= 0 ? 10 : -10; + var xaxis = h( + 'g', + { + attrs: { + class: 'x-axis', + transform: ("translate(" + translateOffsetX + "," + (maxY - 8) + ")") + } + }, + arr.map(function (item, index) { + var labelOffsetX = labelRotate < 0 ? item.x + offsetX : item.x - offsetX; + return h( + 'g', + { + attrs: { + class: 'v-bars--tick', + transform: ("translate(" + labelOffsetX + ",0) rotate(" + labelRotate + ")") + } + }, + [ + h( + 'text', + { + attrs: { + class: 'v-bars--label-text', + style: ("text-anchor:middle; fill:" + labelColor + ";"), + 'font-size': '0.7em', + title: item.title + } + }, + [ + item.label + ] + ) + ] + ) + }) + ); + return rects.concat(xaxis); } var Path = { @@ -144,11 +204,14 @@ var points = genPoints(data, boundary, { max: max, min: min }); var bars = genBars(this, points, h); - return h('g', { - attrs: { - transform: ("scale(1,-1) translate(0,-" + (this.boundary.maxY) + ")") - } - }, bars) + return h( + 'g', + { + class: 'container', + transform: ("translate(0," + (this.boundary.maxY) + ")") + }, + bars + ) } }; @@ -181,6 +244,18 @@ type: Number, default: Infinity }, + minBarHeight: { + type: Number, + default: 3 + }, + labelRotate: { + type: Number, + default: -45 + }, + labelColor: { + type: String, + default: '#999999' + }, height: Number, width: Number, padding: { @@ -201,7 +276,10 @@ minX: padding, minY: padding, maxX: viewWidth - padding, - maxY: viewHeight - padding + maxY: viewHeight - padding, + minBarHeight: this.minBarHeight, + labelRotate: this.labelRotate, + labelColor: this.labelColor }; var props = this.$props; diff --git a/dist/vue-bars.min.js b/dist/vue-bars.min.js index ce912a8..94271ab 100644 --- a/dist/vue-bars.min.js +++ b/dist/vue-bars.min.js @@ -1 +1 @@ -(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global["vue-bars"]=factory())})(this,function(){"use strict";function transitionColor(from,to,count){count=count+1;var int=parseInt(from,16);var intTo=parseInt(to,16);var list=[];var diff=int-intTo;var one=diff/count;list.push(from);for(var i=1;i<=count;i++){list.push(Math.floor(int-one*i).toString(16))}return list}function transition(from,to,count){count=count||3;var r=from.slice(0,2);var g=from.slice(2,4);var b=from.slice(4,6);var rt=to.slice(0,2);var gt=to.slice(2,4);var bt=to.slice(4,6);var allR=transitionColor(r,rt,count);var allG=transitionColor(g,gt,count);var allB=transitionColor(b,bt,count);var list=[];allR.forEach(function(_,i){list.push(""+allR[i]+allG[i]+allB[i])});return list}function generateGradientStepsCss(from,to,count){from=from.replace("#","");to=to.replace("#","");var values=transition(from,to,count);var total=100/(count+1);var obj=[];for(var i=0;i<=count+1;i++){obj.push({percentage:Math.floor(total*i),value:values[i]})}return obj.map(function(value){return"#"+value.value})}function genPoints(inArr,ref,ref$1){var minX=ref.minX;var minY=ref.minY;var maxX=ref.maxX;var maxY=ref.maxY;var max=ref$1.max;var min=ref$1.min;var arr=inArr.map(function(item){return typeof item==="number"?item:item.value});var minValue=Math.min.apply(Math,arr.concat([min]))-.001;var gridX=(maxX-minX)/(arr.length-1);var gridY=(maxY-minY)/(Math.max.apply(Math,arr.concat([max]))+.001-minValue);return arr.map(function(value,index){var title=typeof inArr[index]==="number"?inArr[index]:inArr[index].title;return{x:index*gridX+minX,y:maxY-(value-minValue)*gridY+ +(index===arr.length-1)*1e-5-+(index===0)*1e-5,v:title}})}function genBars(_this,arr,h){var ref=_this.boundary;var maxX=ref.maxX;var maxY=ref.maxY;var totalWidth=maxX/(arr.length-1);if(!_this.barWidth){_this.barWidth=totalWidth-(_this.padding||5)}if(!_this.rounding){_this.rounding=2}var gradients=0;if(_this.gradient&&_this.gradient.length>1){gradients=generateGradientStepsCss(_this.gradient[0],_this.gradient[1],arr.length-1)}var offsetX=(totalWidth-_this.barWidth)/2;return arr.map(function(item,index){return h("rect",{attrs:{id:"bar-id-"+index,fill:gradients?gradients[index]:_this.gradient[0]?_this.gradient[0]:"#000",x:item.x-offsetX,y:0,width:_this.barWidth,height:maxY-item.y,rx:_this.rounding,ry:_this.rounding}},[h("animate",{attrs:{attributeName:"height",from:0,to:maxY-item.y,dur:_this.growDuration+"s",fill:"freeze"}}),h("title",{},[item.v])])})}var Path={props:["data","boundary","barWidth","id","gradient","growDuration","max","min"],render:function render(h){var ref=this;var data=ref.data;var boundary=ref.boundary;var max=ref.max;var min=ref.min;var points=genPoints(data,boundary,{max:max,min:min});var bars=genBars(this,points,h);return h("g",{attrs:{transform:"scale(1,-1) translate(0,-"+this.boundary.maxY+")"}},bars)}};var Bars={name:"Bars",props:{data:{type:Array,required:true},autoDraw:Boolean,barWidth:{type:Number,default:8},growDuration:{type:Number,default:.5},gradient:{type:Array,default:function(){return["#000"]}},max:{type:Number,default:-Infinity},min:{type:Number,default:Infinity},height:Number,width:Number,padding:{type:Number,default:8}},render:function render(h){if(!this.data||this.data.length<2){return}var ref=this;var width=ref.width;var height=ref.height;var padding=ref.padding;var viewWidth=width||300;var viewHeight=height||75;var boundary={minX:padding,minY:padding,maxX:viewWidth-padding,maxY:viewHeight-padding};var props=this.$props;props.boundary=boundary;props.id="vue-bars-"+this._uid;return h("svg",{attrs:{width:width||"100%",height:height||"25%",viewBox:"0 0 "+viewWidth+" "+viewHeight}},[h(Path,{props:props,ref:"path"})])}};Bars.install=function(Vue){Vue.component(Bars.name,Bars)};if(typeof window!=="undefined"&&window.Vue){window.Vue.use(Bars)}return Bars}); +(function(global,factory){typeof exports==="object"&&typeof module!=="undefined"?module.exports=factory():typeof define==="function"&&define.amd?define(factory):(global=global||self,global["vue-bars"]=factory())})(this,function(){"use strict";function transitionColor(from,to,count){count=count+1;var int=parseInt(from,16);var intTo=parseInt(to,16);var list=[];var diff=int-intTo;var one=diff/count;list.push(from);for(var i=1;i<=count;i++){list.push(Math.floor(int-one*i).toString(16))}return list}function transition(from,to,count){count=count||3;var r=from.slice(0,2);var g=from.slice(2,4);var b=from.slice(4,6);var rt=to.slice(0,2);var gt=to.slice(2,4);var bt=to.slice(4,6);var allR=transitionColor(r,rt,count);var allG=transitionColor(g,gt,count);var allB=transitionColor(b,bt,count);var list=[];allR.forEach(function(_,i){list.push(""+allR[i]+allG[i]+allB[i])});return list}function generateGradientStepsCss(from,to,count){from=from.replace("#","");to=to.replace("#","");var values=transition(from,to,count);var total=100/(count+1);var obj=[];for(var i=0;i<=count+1;i++){obj.push({percentage:Math.floor(total*i),value:values[i]})}return obj.map(function(value){return"#"+value.value})}function genPoints(inArr,ref,ref$1){var minX=ref.minX;var minY=ref.minY;var maxX=ref.maxX;var maxY=ref.maxY;var minBarHeight=ref.minBarHeight;var labelRotate=ref.labelRotate;var max=ref$1.max;var min=ref$1.min;var arr=inArr.map(function(item){return typeof item==="number"?item:item.value});var minValue=Math.min.apply(Math,arr.concat([min]));var maxValue=Math.max.apply(Math,arr.concat([max]));var absMaxVal=Math.abs(maxValue);var absMinVal=Math.abs(minValue);var gridX=(maxX-minX)/(arr.length-1);var labelHeight=20;var delta=0;if(minValue<0&&maxValue<0){delta=absMinVal}else if(minValue<0&&maxValue>=0){delta=absMinVal+absMaxVal}else if(minValue>=0&&maxValue>=0){delta=maxValue}var heightMultiplier=delta!==0?(maxY-minY-labelHeight)/delta:1;var yAdjust=minValue*heightMultiplierminBarHeight?height*heightMultiplier-yAdjust:minBarHeight;return{x:index*gridX+minX,y:maxY-barHeight-(value>=0||value===0&&minValue>=0?zeroLine*heightMultiplier:zeroLine*heightMultiplier-barHeight)-labelHeight-yAdjust,height:barHeight,label:label,title:title}})}function genBars(_this,arr,h){var ref=_this.boundary;var maxX=ref.maxX;var maxY=ref.maxY;var labelRotate=ref.labelRotate;var labelColor=ref.labelColor;var totalWidth=maxX/(arr.length-1);if(!_this.barWidth){_this.barWidth=totalWidth-(_this.padding||5)}if(!_this.rounding){_this.rounding=2}var gradients=0;if(_this.gradient&&_this.gradient.length>1){gradients=generateGradientStepsCss(_this.gradient[0],_this.gradient[1],arr.length-1)}var offsetX=(totalWidth-_this.barWidth)/2;var rects=arr.map(function(item,index){return h("rect",{attrs:{id:"bar-id-"+index,fill:gradients?gradients[index]:_this.gradient[0]?_this.gradient[0]:"#000",x:item.x-offsetX,y:item.y,width:_this.barWidth,height:item.height,rx:_this.rounding,ry:_this.rounding}},[h("animate",{attrs:{attributeName:"height",from:0,to:item.height,dur:_this.growDuration+"s",fill:"freeze"}}),h("title",{},[item.title])])});var translateOffsetX=labelRotate>=0?10:-10;var xaxis=h("g",{attrs:{class:"x-axis",transform:"translate("+translateOffsetX+","+(maxY-8)+")"}},arr.map(function(item,index){var labelOffsetX=labelRotate<0?item.x+offsetX:item.x-offsetX;return h("g",{attrs:{class:"v-bars--tick",transform:"translate("+labelOffsetX+",0) rotate("+labelRotate+")"}},[h("text",{attrs:{class:"v-bars--label-text",style:"text-anchor:middle; fill:"+labelColor+";","font-size":"0.7em",title:item.title}},[item.label])])}));return rects.concat(xaxis)}var Path={props:["data","boundary","barWidth","id","gradient","growDuration","max","min"],render:function render(h){var ref=this;var data=ref.data;var boundary=ref.boundary;var max=ref.max;var min=ref.min;var points=genPoints(data,boundary,{max:max,min:min});var bars=genBars(this,points,h);return h("g",{class:"container",transform:"translate(0,"+this.boundary.maxY+")"},bars)}};var Bars={name:"Bars",props:{data:{type:Array,required:true},autoDraw:Boolean,barWidth:{type:Number,default:8},growDuration:{type:Number,default:.5},gradient:{type:Array,default:function(){return["#000"]}},max:{type:Number,default:-Infinity},min:{type:Number,default:Infinity},minBarHeight:{type:Number,default:3},labelRotate:{type:Number,default:-45},labelColor:{type:String,default:"#999999"},height:Number,width:Number,padding:{type:Number,default:8}},render:function render(h){if(!this.data||this.data.length<2){return}var ref=this;var width=ref.width;var height=ref.height;var padding=ref.padding;var viewWidth=width||300;var viewHeight=height||75;var boundary={minX:padding,minY:padding,maxX:viewWidth-padding,maxY:viewHeight-padding,minBarHeight:this.minBarHeight,labelRotate:this.labelRotate,labelColor:this.labelColor};var props=this.$props;props.boundary=boundary;props.id="vue-bars-"+this._uid;return h("svg",{attrs:{width:width||"100%",height:height||"25%",viewBox:"0 0 "+viewWidth+" "+viewHeight}},[h(Path,{props:props,ref:"path"})])}};Bars.install=function(Vue){Vue.component(Bars.name,Bars)};if(typeof window!=="undefined"&&window.Vue){window.Vue.use(Bars)}return Bars}); diff --git a/src/components/bars.js b/src/components/bars.js index 75c870b..adaf61c 100644 --- a/src/components/bars.js +++ b/src/components/bars.js @@ -29,6 +29,18 @@ export default { type: Number, default: Infinity }, + minBarHeight: { + type: Number, + default: 3 + }, + labelRotate: { + type: Number, + default: -45 + }, + labelColor: { + type: String, + default: '#999999' + }, height: Number, width: Number, padding: { @@ -46,7 +58,10 @@ export default { minX: padding, minY: padding, maxX: viewWidth - padding, - maxY: viewHeight - padding + maxY: viewHeight - padding, + minBarHeight: this.minBarHeight, + labelRotate: this.labelRotate, + labelColor: this.labelColor } const props = this.$props diff --git a/src/components/path.js b/src/components/path.js index 34c1466..1d4cce5 100644 --- a/src/components/path.js +++ b/src/components/path.js @@ -8,10 +8,13 @@ export default { const points = genPoints(data, boundary, { max, min }) const bars = genBars(this, points, h) - return h('g', { - attrs: { - transform: `scale(1,-1) translate(0,-${this.boundary.maxY})` - } - }, bars) + return h( + 'g', + { + class: 'container', + transform: `translate(0,${this.boundary.maxY})` + }, + bars + ) } } diff --git a/src/helpers/path.js b/src/helpers/path.js index d3afac8..fc84d99 100644 --- a/src/helpers/path.js +++ b/src/helpers/path.js @@ -6,28 +6,45 @@ import { generateGradientStepsCss } from './gradient' * @param {object} boundary * @return {object[]} */ -export function genPoints (inArr, { minX, minY, maxX, maxY }, { max, min }) { +export function genPoints (inArr, { minX, minY, maxX, maxY, minBarHeight, labelRotate }, { max, min }) { const arr = inArr.map(item => (typeof item === 'number' ? item : item.value)) - const minValue = Math.min(...arr, min) - 0.001 + const minValue = Math.min(...arr, min) + const maxValue = Math.max(...arr, max) + const absMaxVal = Math.abs(maxValue) + const absMinVal = Math.abs(minValue) const gridX = (maxX - minX) / (arr.length - 1) - const gridY = (maxY - minY) / (Math.max(...arr, max) + 0.001 - minValue) + const labelHeight = 20 + + let delta = 0 + if (minValue < 0 && maxValue < 0) { + delta = absMinVal + } else if (minValue < 0 && maxValue >= 0) { + delta = absMinVal + absMaxVal + } else if (minValue >= 0 && maxValue >= 0) { + delta = maxValue + } + + const heightMultiplier = delta !== 0 ? (maxY - minY - labelHeight) / delta : 1 + const yAdjust = minValue * heightMultiplier < minBarHeight ? minBarHeight : 0; + const zeroLine = minValue < 0 ? absMinVal : 0 return arr.map((value, index) => { - const title = typeof inArr[index] === 'number' ? inArr[index] : inArr[index].title + const label = typeof inArr[index].title !== 'undefined' ? inArr[index].title : '' + const title = typeof inArr[index].value === 'number' ? inArr[index].value : inArr[index] + const height = Math.abs(value) + const barHeight = (height * heightMultiplier - yAdjust > minBarHeight ? height * heightMultiplier - yAdjust : minBarHeight) return { x: index * gridX + minX, - y: - maxY - - (value - minValue) * gridY + - +(index === arr.length - 1) * 0.00001 - - +(index === 0) * 0.00001, - v: title + y: maxY - barHeight - (value >= 0 || value === 0 && minValue >= 0 ? zeroLine * heightMultiplier : zeroLine * heightMultiplier - barHeight) - labelHeight - yAdjust, + height: barHeight, + label: label, + title: title } }) } export function genBars (_this, arr, h) { - const { maxX, maxY } = _this.boundary + const { maxX, maxY, labelRotate, labelColor } = _this.boundary const totalWidth = (maxX) / (arr.length - 1) if (!_this.barWidth) { _this.barWidth = totalWidth - (_this.padding || 5) @@ -42,15 +59,15 @@ export function genBars (_this, arr, h) { } const offsetX = (totalWidth - _this.barWidth) / 2 - return arr.map((item, index) => { + const rects = arr.map((item, index) => { return h('rect', { attrs: { id: `bar-id-${index}`, fill: (gradients ? gradients[index] : (_this.gradient[0] ? _this.gradient[0] : '#000')), x: item.x - offsetX, - y: 0, + y: item.y, width: _this.barWidth, - height: (maxY - item.y), + height: item.height, rx: _this.rounding, ry: _this.rounding } @@ -59,12 +76,51 @@ export function genBars (_this, arr, h) { attrs: { attributeName: 'height', from: 0, - to: (maxY - item.y), + to: item.height, dur: `${_this.growDuration}s`, fill: 'freeze' } }), - h('title', {}, [item.v]) + h('title', {}, [item.title]) ]) }) + const translateOffsetX = labelRotate >= 0 ? 10 : -10 + const xaxis = h( + 'g', + { + attrs: { + class: 'x-axis', + transform: `translate(${translateOffsetX},${maxY - 8})` + } + }, + arr.map((item, index) => { + const labelOffsetX = labelRotate < 0 ? item.x + offsetX : item.x - offsetX; + return h( + 'g', + { + attrs: { + class: 'v-bars--tick', + transform: `translate(${labelOffsetX},0) rotate(${labelRotate})` + } + }, + [ + h( + 'text', + { + attrs: { + class: 'v-bars--label-text', + style: `text-anchor:middle; fill:${labelColor};`, + 'font-size': '0.7em', + title: item.title + } + }, + [ + item.label + ] + ) + ] + ) + }) + ) + return rects.concat(xaxis); }