Public
Edited
Nov 15, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function draw_legend(div){
const legendDiv = div.append('div')
.style('display','flex')
.style('gap', '8px')
.style('justify-content', 'center')
.style('padding-bottom', '16px')
.style('overflow','hidden')
.style('flex-wrap', 'wrap')

legendDiv.append('div')
.style('display','flex')
.style('flex-wrap', 'wrap')
.style('gap', '8px')
.selectAll('div')
.data(colorScale_Bars.domain())
.join('div')
.style('display','flex')
.style('align-items', 'center')
.style('gap', '4px')
.call(div=>{
div.append('svg')
.attr('width',12)
.attr('height',12)
.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('rx',2)
.attr('ry',2)
.attr('fill', d=>colorScale_Bars(d))

})
.call(div=>{
div.append('div')
.text(d=>d)
.style("font", "12px sans-serif")
.style("font-weight", '500')
.style('color', '#646A73')
.style('overflow','hidden')
.style('text-overflow','ellipsis')
})


legendDiv.append('div')
.style('display','flex')
.style('flex-wrap', 'wrap')
.style('gap', '8px')
.selectAll('div')
.data(colorScale_Bars.domain())
.join('div')
.style('display','flex')
.style('align-items', 'center')
.style('gap', '4px')
.call(div=>{
div.append('svg')
.attr('width',14)
.attr('height',3)
.append('rect')
.attr('width', '100%')
.attr('height', '100%')
.attr('rx',2)
.attr('ry',2)
.attr('fill', d=>colorScale_Lines(d))

})
.call(div=>{
div.append('div')
.text(d=>d+' 占比')
.style("font", "12px sans-serif")
.style("font-weight", '500')
.style('color', '#646A73')
.style('overflow','hidden')
.style('text-overflow','ellipsis')
})
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
function draw_combination_chart(div){
const container = div.append('div')
.style('display','flex')
.style('flex-direction', 'column')

const title = container.append('div')
.append('p')
.text('离职人数统计')
.style('margin', '0 0 0 0')
.style('padding', '18px 16px')
.style('font-family', 'sans-serif')
.style('font-size', '14px')
.style('font-weight', 500)
.style('color', '#1F2329')

const svg = container.append('div')
.style('display','flex')
.style('flex-direction', 'column')
.append('svg')
.attr('width', layout.svgWidth)
.attr('height', layout.svgHeight)
const g = svg.append('g').attr('class','svg-g').attr('transform', `translate(${layout.margin.left}, ${layout.margin.top})`)

g.append('g')
.attr('class', 'x-axis-g')
.attr('transform', `translate(0, ${layout.innerHeight})`)
.call(xAxis)

g.append('g')
.attr('class', 'y-axis-left-g')
.call(yAxisLeft)
.call(g=>{
g.selectAll('.tick line').clone()
.attr("x2", layout.innerWidth)
.attr("stroke-opacity", 0.1)
})

g.append('g')
.attr('class', 'y-axis-left-g')
.attr('transform', `translate( ${layout.innerWidth}, 0)`)
.call(yAxisRight)


const bars_g = g.append('g')

if(chartAttrs.barType == 'Grouped'){
bars_g.attr('class', 'grouped-bars-g')
.selectAll('g')
.data(stacked_data)
.join('g')
.attr('class', 'grouped-g')
.attr('transform', ([name, array])=>{
return `translate(${xScaleBand(name)},0)`
})
.selectAll('rect')
.data(d=>d[1])
.join('rect')
.attr('x', ({key})=>{
return innerXScaleBand(key)
})
.attr('y', ({value})=>yScale_Bars(value))
.attr('width', innerXScaleBand.bandwidth())
.attr('height', ({value})=>layout.innerHeight - yScale_Bars(value))
.attr('fill', ({key})=>colorScale_Bars(key))

}else if(chartAttrs.barType == 'Stacked'){
bars_g.attr('class', 'stacked-bars-g')
.selectAll('g')
.data(stack(stacked_data))
.join('g')
.attr('class', 'stack-layer-g')
.attr('fill', d=>{
// console.log(d['key'])
return colorScale_Bars(d['key'])
})
.selectAll('rect')
.data(d=>d)
.join('rect')
.attr('x', d=>{
// console.log(d)
// console.log(d['data'][0])
return xScaleBand(d['data'][0])
})
.attr('y', ([y1,y2])=> yScale_Bars(y2))
.attr('width', d=>xScaleBand.bandwidth())
.attr('height', ([y1,y2])=> yScale_Bars(y1)-yScale_Bars(y2))
}

let lines_g = g.append('g').attr('class', 'lines-g')
.selectAll('path')
.data(data)
.join('path')
.attr("fill", "none")
.attr('stroke-width', chartAttrs.line_width)
.attr('stroke-linejoin', 'round')
.attr('stroke-linecap', 'round')
.attr("stroke", d=>{
return colorScale_Lines(d[0])
})
.attr('d', d=>line(d[1]))

container.call(draw_legend)
}
Insert cell
Insert cell
colorScale_Bars.domain()
Insert cell
Insert cell
data = d3.groups(bars_sample_data, d=>d['key'])
.slice(0, chartAttrs.innerFirstN)
.map(([name, array])=>[name, array.slice(0, chartAttrs.outerFirstN)])
Insert cell
sum = d3.sum(data, ([name, array])=> d3.sum(array, d=>d['value']))
Insert cell
max = d3.max(data, ([name, array])=> d3.max(array, d=>d['value']))
Insert cell
Insert cell
Insert cell
line = d3.line()
.x(d=>{
// console.log(d['name'])
// console.log(xScale(d['name']))
return xScalePoint(d['name'])
})
.y(d=>{
// console.log(d['value'])
// console.log(d['value']/max)
// console.log(yScale_Lines(d['value']/max))
return yScale_Lines(d['value']/sum)
})
Insert cell
line(data[0][1])
Insert cell
yScale_Lines.domain()
Insert cell
Insert cell
stacked_data = d3.groups(bars_sample_data, d=>d['name'])
.slice(0, chartAttrs.outerFirstN)
.map(([name, array])=>[name, array.slice(0, chartAttrs.innerFirstN)])
Insert cell
stacked_max = d3.max(stacked_data, ([name, array])=> d3.sum(array, d=>d['value']))
Insert cell
yScale_Bars.domain()
Insert cell
keys = stacked_data[0][1].map(({key})=>key)
Insert cell
stack = d3.stack()
.keys(keys)
.value(([arrayKey, array], key)=>{
// console.log(array)
// console.log(key)
// console.log(array.find(e=>e['key']==key))
// console.log(array.find(e=>e['key']==key)['value'])
return array.find(e=>e['key']==key)['value']
})
Insert cell
stack(stacked_data)
Insert cell
Insert cell
function colorInterpolate(colorArray, {type = 'default'} = {}){
// 构建一个渐变函数对象,用节点作为key
const colorFuncObj = {};
for (let i = 1; i < colorArray.length; ++i) {
colorFuncObj[`${i}`] = [colorArray[i-1], colorArray[i]]
}

// 返回一个函数
return function(number) {
if(number<0 || number>1){
return '#fff'
}

let mappedNumber = number * (colorArray.length - 1)

//根据 前景色 或者 背景色 映射
if(type == 'default'){
}else if(type == 'front'){
mappedNumber = mappedNumber * colorPercentage.frontColor
// console.log('front color')
}else if(type == 'background'){
mappedNumber = mappedNumber * colorPercentage.backgroundColor + (1 - colorPercentage.backgroundColor) * (colorArray.length - 1)
// console.log('background color')
}else{
throw new Error('"type" is illegal')
}

for (const key in colorFuncObj){
if((mappedNumber >= +key-1)&&(mappedNumber < +key)){
const ratio = mappedNumber - Math.trunc(mappedNumber)
const [color1, color2] = colorFuncObj[`${key}`]
return d3.color(d3.interpolate(color1, color2)(ratio)).formatHex()
}else if(mappedNumber == colorArray.length-1){
return colorArray[colorArray.length - 1]
}
}
}
}
Insert cell
function colorSampling(colorArray,n, {type = 'default'} = {}){
if(n == 1){
return [colorInterpolate(colorArray, {type})(0.35)]
}else if(n == 2){
return [colorInterpolate(colorArray, {type})(0.25),
colorInterpolate(colorArray, {type})(0.6)]
}else if(n == 3){
return [colorInterpolate(colorArray, {type})(0.2),
colorInterpolate(colorArray, {type})(0.45),
colorInterpolate(colorArray, {type})(0.8)]
}else if(n == 4){
return [colorInterpolate(colorArray, {type})(0.1),
colorInterpolate(colorArray, {type})(0.35),
colorInterpolate(colorArray, {type})(0.6),
colorInterpolate(colorArray, {type})(0.85)]
}else{
return d3.quantize(colorInterpolate(colorArray, {type}),n)
}
}
Insert cell
Insert cell
Insert cell
line_colors = newColorData.filter(e=>e['Source']===colorScheme.lineColors).map(e=>e['Hex'])
Insert cell
bar_colors = newColorData.filter(e=>e['Source']===colorScheme.barColors).map(e=>e['Hex'])
Insert cell
samplingNum = data.length
Insert cell
colorScale_Bars = d3.scaleOrdinal()
.domain(data.map(([name, array])=>name))
.range(colorSampling(bar_colors, samplingNum, {type: 'front'}))
Insert cell
colorScale_Lines = d3.scaleOrdinal()
.domain(data.map(([name, array])=>name))
.range(colorSampling(line_colors, samplingNum, {type: 'front'}))
Insert cell
Insert cell
stacked_data.map(([e, array])=>e)
Insert cell
stacked_data[0][1].map(({key})=>key)
Insert cell
xScaleBand ={
let paddingInner
if(chartAttrs.barType == 'Grouped'){
paddingInner = chartAttrs.paddingInner
}else if(chartAttrs.barType == 'Stacked'){
paddingInner = chartAttrs.stackedPaddingInner
}
return d3.scaleBand()
.domain(stacked_data.map(([e, array])=>e))
.range([0, layout.innerWidth])
.paddingInner(paddingInner)
.paddingOuter(chartAttrs.paddingOuter)
}
Insert cell
innerXScaleBand = d3.scaleBand()
.domain(stacked_data[0][1].map(({key})=>key))
.range([0, xScaleBand.bandwidth()])
.paddingInner(chartAttrs.paddingInner_2)
.paddingOuter(chartAttrs.paddingOuter_2)
Insert cell
xScalePoint = d3.scalePoint()
.domain(data[0][1].map(({name})=>name))
.range([0, layout.innerWidth])
.padding(chartAttrs.padding)
Insert cell
yScale_Bars = {
let domainMax
if(chartAttrs.barType == 'Grouped'){
domainMax = max * barHeightPercentage
}else if(chartAttrs.barType == 'Stacked'){
domainMax = stacked_max * barHeightPercentage
}

return d3.scaleLinear()
.domain([0, domainMax])
.range([layout.innerHeight, 0])
}
Insert cell
yScale_Lines = d3.scaleLinear()
.domain([0, max/sum])
.range([layout.innerHeight, 0])
Insert cell
Insert cell
xAxis = d3.axisBottom().scale(xScaleBand)
Insert cell
yAxisLeft = d3.axisLeft().scale(yScale_Bars).ticks(5)
Insert cell
yAxisRight = d3.axisRight().scale(yScale_Lines).ticks(5)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
bars_sample_data = sample_data.filter(e=>e['chartType']== 'donuts_groupedbars')
Insert cell
newColorData = d3.csv(getCsvUrl(url_new_color), (e)=>{
if(e['Hex'].trim().charAt(0)==='#'){
return {
Source: `${e['Source']} - ${e['Serise']}`,
SourceOrigin: e['Serise'],
Serise: e['Serise'],
Hex: e['Hex'],
Grads: +e['Grads'],
Type: e['Source'],
Serise: e['Serise']
}
}else{
return {
Source: `${e['Source']} - ${e['Serise']}`,
SourceOrigin: e['Serise'],
Serise: e['Serise'],
Hex: '#'+e['Hex'],
Grads: +e['Grads'],
Type: e['Source'],
Serise: e['Serise']
}
}
})
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more