Open dlrandy opened 6 years ago
<!DOCTYPE html>
import React from 'react'; import ReactDOM from 'react-dom'; import * as d3 from 'd3';
import './index.css'; import { regionColors, choiceColors } from '../../../utils/Colors.js'; import { getComputedPercentage, wrap, dotme, responsivefy2nd, getWrapLinesNum, debounce, wrapLines } from '../../../utils/Helper.js'; class SurveyBar extends React.Component {
static defaultProps = { width: 500, height: 500, data: {}, }; constructor(props) { super(props); this.resized = false; // this.svgRef = React.createRef(); }
createBarChart(){ const { isResponse, } = this.props;
let {
percents = [],
texts = [],
numbers = [],
totalCount = 0,
} = this.props.data;
let rx = 0,
ry = 0,
areaHeight = d3.select(this.svgRef).attr('height') * 0.7,
lineHeight = -10;
let voteLenScale = d3.scaleLinear()
.domain([0, totalCount])
.range([0, 1]);
let voteLineScale = d3.scaleLinear()
.domain([0, texts.length + 1])
.range([0, areaHeight]);
let $voteDataArea = d3.select(this.svgRef);
$voteDataArea.selectAll('*').remove();
function generateRect($selection) {
let ele = $selection
.append('rect');
ele.attr('rx', rx)
.attr('ry', ry)
.attr('height', 28)
.attr('width', 0)
.attr('y', function (d, i) {
return voteLineScale(i + 1);
})
.attr('x', 0)
.attr('fill', 'lightgray');
return ele;
}
let $backgroundBar = $voteDataArea.selectAll('.backgroundbar')
.data(texts)
.enter();
generateRect($backgroundBar)
.attr('class', 'backgroundbar')
.attr('width', function (d, i) {
return '100%';
});
let $votesBar = $voteDataArea.selectAll('.votesbar')
.data(numbers)
.enter();
generateRect($votesBar)
.attr('fill', function (d, i) {
return isResponse ? regionColors[texts[i]] : choiceColors[i];
})
.transition()
.delay(100)
.duration(500)
.attr('width', function (d, i) {
return voteLenScale(d) * 100 + '%';
});
function createText($selection) {
let $text = $selection
.append("text");
$text.attr('y', function (d, i) {
return voteLineScale(i + 1) + lineHeight;
})
.attr('x', 0)
.attr('lengthAdjust', "spacingAndGlyphs")
.attr("font-size", "15px")
.attr("fill", "#07c");
return $text;
}
function createTitle($selection) {
let $group = $selection
.append("g");
let $title = $group
.append("text");
$title.attr('y', function (d, i) {
return voteLineScale(i + 1) + lineHeight;
})
.attr('x', 0)
.attr('lengthAdjust', "spacingAndGlyphs")
.attr("font-size", "15px")
.attr("fill", "#07c");
$group.append('title')
.attr('class', 'tip')
.text(function (d) {
return d;
})
.attr('x', 48)
.attr('fill', '#333a40');
return $title;
}
let $voteCountSelection = $voteDataArea.selectAll('text.count')
.data(numbers)
.enter();
createText($voteCountSelection)
.attr('class', 'count')
.attr('x', 10)
.attr('y', function (d, i) {
return voteLineScale(i + 1) + 18;
})
.attr('fill', isResponse ? 'rgba(0,0,0,0)' : 'white')
.text(function (d) {
return d;
});
let $percentSelection = $voteDataArea.selectAll('text.percent')
.data(percents)
.enter();
createText($percentSelection)
.attr('class', 'percent')
.text(function (d) {
return getComputedPercentage(d);
});
let $titleSelection = $voteDataArea.selectAll('text.title')
.data(texts)
.enter();
createTitle($titleSelection)
.attr('class', 'title')
.text(function (d) {
return d;
})
.attr('x', 60)
.attr('width', 450)
.attr('fill', '#333a40');
function dotme(text) {
text.each(function () {
var text = d3.select(this);
var words = text.text().split('');
var ellipsis = text.text('').append('tspan').attr('class', 'elip').text('...');
var width = parseFloat(text.attr('width')) - ellipsis.node().getComputedTextLength();
var numWords = words.length;
var tspan = text.insert('tspan', ':first-child').text(words.join(''));
while (tspan.node().getComputedTextLength() > width && words.length) {
tspan.text(words.join('').substr(0, width - 4).trim());
words.pop();
}
if (words.length === numWords) {
ellipsis.remove();
}
});
}
// $voteDataArea.selectAll('text.title').call(wrap);
$voteDataArea.selectAll('text.title').call(dotme);
} componentDidMount(){ this.createBarChart3rd(); this.resized = true; this.debounceFunc = debounce( this.createBarChart3rd, 100, true//nocallnow ); window.addEventListener('resize', this.debounceFunc); } componentWillUnmount(){ window.removeEventListener('resize', this.debounceFunc); } render() { const { survey } = this.props; let { texts = [], } = this.props.data; const isCrossMinWidth = window.innerWidth <= 750; let width = isCrossMinWidth ? window.innerWidth : window.innerWidth / 3; width = width - 40; const height = 54 * d3.max([texts.length, 5]); return ( <svg ref={node => this.svgRef = node} width={width} height={height} preserveAspectRatio="xMidYMid meet"> ); }
componentDidUpdate(prevProps, prevState, snapshot){
if(prevProps.survey.id !== this.props.survey.id){
this.resized = false;
this.createBarChart3rd();
}
this.resized = true;
} createBarChart3rd = () => { const { isResponse, } = this.props;
let {
percents = [],
texts = [],
numbers = [],
totalCount = 0,
} = this.props.data;
const svg = d3.select(this.svgRef);
svg.selectAll('*').remove();
const margin = 20;
const rateLineHeight = 1.2;
const fontSzie = 16;
const isCrossMinWidth = window.innerWidth <= 750;
let width = isCrossMinWidth ? window.innerWidth : window.innerWidth / 3;
width = width - 2 * margin;
const lineNumsForText = texts.map((t) => wrapLines(t, width));
const actualLines = lineNumsForText.filter(t => t > 1);
console.log(lineNumsForText)
const height = 54 * texts.length ;
const chart = svg.append('g')
// .attr('transform', `translate(${margin}, ${margin})`);
const yScale = d3.scaleBand()
.range([height, 0])
.domain(texts)
.padding(0.6)
const xScale = d3.scaleLinear()
.range([0, width])
.domain([0, 1]);
const barGroups = chart.selectAll()
.data(percents)
.enter()
.append('g')
barGroups
.append('rect')
.attr('class', 'back-bar')
.attr('x', (g) => 0)
.attr('y', (g, i) => {
let nums = 0;
for(let j = 0; j <= i; j++){
nums += lineNumsForText[j]
}
return nums * yScale.bandwidth()+ yScale(texts[i]);
// return yScale(texts[i]);
})
.attr('width', (g) => width)
.attr('height', yScale.bandwidth())
const rectGroups = chart.selectAll() .data(percents) .enter() .append('g')
const rects = rectGroups .append('rect') .attr('class', 'bar') .attr('x', (g) => 0) .attr('y', (g, i) => { let nums = 0; for(let j = 0; j <= i; j++){ nums += lineNumsForText[j] }
return height - yScale(texts[i]) - yScale.bandwidth() + nums * yScale.bandwidth();
})
.attr('fill', function (d, i) {
return isResponse ? regionColors[texts[i]] : choiceColors[i];
})
.attr('height', yScale.bandwidth())
const transitions = this.resized ? rects : rects.transition().duration(1000);
transitions.attr('width', (g, i) =>{
return xScale(g) >= width ? width : xScale(g) ;//;
})
barGroups
.append('text')
.attr('class', 'value')
.attr('y', (a, i) => height - yScale(texts[i]) - yScale.bandwidth() * rateLineHeight )
.attr('x', (a) => 0 )
.attr('text-anchor', 'start')
.style('font-size', fontSzie)
.text((a) => `${getComputedPercentage(a)}`)
.attr('fill', '#07c')
const barLabelGroups = chart.selectAll() .data(texts) .enter() .append('g');
const barLabels = barLabelGroups
.append('text')
.attr('class', 'label')
.attr('y', (a) => height - yScale(a) - yScale.bandwidth() * rateLineHeight )
.attr('x', (a) => 0 + 60)
.attr('text-anchor', 'start')
.attr('fill', '#333a40')
.style('font-size', fontSzie)
.text((a) => ${a}
)
barLabelGroups
.append('title')
.attr('class', 'tip')
.text(function (d) {
return d;
})
barLabels.call(wrap, width - 2.5 * margin)
const barTextGroups = chart.selectAll() .data(texts) .enter() .append('g')
barTextGroups
.append('text')
.attr('class', 'pp-value')
.attr('y', (a) => height - yScale(a) - 5)
.attr('x', (a) => 5 )
.attr('fill', 'red')
.attr('text-anchor', 'start')
.text((a, i) => `${numbers[i]}`)
.style('font-size', fontSzie)
.attr('fill', isResponse ? 'rgba(0,0,0,0)' : 'white')
// svg.call(responsivefy2nd);
} } export default SurveyBar;
import as moment from "moment"; import as d3 from 'd3';
export function formatName(surveyName){ let index = surveyName.lastIndexOf('_'); if(index === -1) { return surveyName; } index++; const timestamp = new Date(parseInt(surveyName.substring(index))); return surveyName.substring(0, index) + moment.default(timestamp).format('MMDDYYYY') ; }
/**
@return {number|string} - this is a queryString, may be undefined */ export function getQueryId(key = 'id'){ let query = location.search.replace(/^(\?)/, ''), res = {}; query.split('&').forEach(param => { const parts = param.replace(/+/g, ' ').split('='); const key = parts.shift(); const val = parts.join('='); res[key] = val; });
return res[key]; }
export function debounce(func, wait, nocallnow){ var timeout = nocallnow; return function exeFunc(val){ var callNow = !timeout; timeout = null; clearTimeout(timeout); timeout = setTimeout(() => func(val), wait); if(callNow){ func(val); } } } export function b64DecodeUnicode(str) { // Going backwards: from bytestream, to percent-encoding, to original string. return decodeURIComponent(atob(str).split('').map(function(c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); }
export function formatDateStr( timestamp ){ return moment.default(timestamp).format("MMM DD, YYYY") } export function formatDateString( timestamp ){ return moment.default(timestamp).format("MMMM DD, YYYY") }
export function getTimeRemaining(endtime){ let t = endtime - Date.now(); let seconds = Math.floor( (t/1000) % 60 ); let minutes = Math.ceil( (t/1000/60) % 60 ); let hours = Math.floor( (t/(10006060)) % 24 ); let days = Math.floor( t/(10006060*24) ); return { 'total': t, 'days': days , 'hours': hours, 'minutes': minutes, 'seconds': seconds }; }
export const isIE11 = !!window.MSInputMethodContext && !!document.documentMode; // true on IE11 // false on Edge and other IEs/browsers.
export function responsivefy(svg, isRes) { var container = d3.select(svg.node().parentNode), width = parseInt(svg.style("width")) (isRes ? 1.8 : 1.8), height = parseInt(svg.style("height")) (isRes ? 1.9 : 2), aspect = width / height; svg .attr("viewBox", "0 0 " + width + " " + height) .attr("perserveAspectRatio", "xMinYMid") .call(resize); d3.select(window).on("resize." + container.attr("id") + (isRes ? 'rse' : ''), resize);
function resize() { var targetWidth = parseInt(container.style("width")) || false; if(targetWidth){ svg.attr("width", targetWidth); svg.attr("height", Math.round(targetWidth / aspect)); }
isRes ? d3.select(svg.node().querySelector('#res-canvas')).style('transform', 'translate(48%,43%)') : null;
} }
export function getComputedPercentage(num){ return (num 1000000 / 10000).toFixed(2) + '%'; } export function responsivefy2nd(svg) { var container = d3.select(svg.node().parentNode), width = parseInt(svg.style("width")),// 1.5, height = parseInt(svg.style("height")),// * 1.2, aspect = width / height; console.log(svg.node(), container.node()) svg .attr("viewBox", "0 0 " + width + " " + height) .attr("perserveAspectRatio", "xMinYMid") .call(resize); console.log("resize." + container.attr("id"), 'listen') d3.select(window).on("resize." + container.attr("id"), resize);
function resize() { const isCrossMinWidth = window.innerWidth <= 750; let width = isCrossMinWidth ? window.innerWidth : window.innerWidth / 3;
var targetWidth = parseInt(container.style("width"));
targetWidth = targetWidth ? targetWidth - 10 : width;
svg.attr("width", targetWidth);
svg.attr("height", Math.round(targetWidth / aspect));
} }
export function wrap(text, width) { // var wrapLines = []; text.each(function () { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy")) || 1.2, tspan = text.text(null).append("tspan").attr("x", 60).attr("y", y); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan").attr("x", 60).attr("y", y).attr("dy", ++lineNumber * lineHeight + "em").text(word); } } }); }
export function wrapLines(text, width) { let textNode = document.getElementById('spanForSvg'); textNode.textContent = text; let textWidth = textNode.offsetWidth; return Math.ceil(textWidth / width) - 1; }
export function dotme(text) { text.each(function () { var text = d3.select(this); var words = text.text().split('');
var ellipsis = text.text('').append('tspan').attr('class', 'elip').text('...');
const isCrossMinWidth = window.innerWidth <= 750;
let www = isCrossMinWidth ? window.innerWidth : window.innerWidth / 3;
var width = www - 97 - ellipsis.node().getComputedTextLength();
var numWords = words.length;
var tspan = text.insert('tspan', ':first-child').text(words.join(''));
while (tspan.node().getComputedTextLength() > width && words.length) {
tspan.text(words.join('').substr(0, width - 3).trim());
words.pop();
}
if (words.length === numWords) {
ellipsis.remove();
}
});
}
let percents = [0.1, 0.2, 0.7], texts = ["aaaa", "bbb", "ccc"], numbers = [10, 20, 70], totalCount = 100; const isCrossMinWidth = window.innerWidth <= 750; const fixHeight = 500; let width = isCrossMinWidth ? window.innerWidth : window.innerWidth / 3; let height = isCrossMinWidth ? window.innerHeight : window.innerHeight / 3;
let cDim = { height: height, width: width, innerRadius: width 0.1, outerRadius: width 0.4, labelRadius: width * 0.6 };
function createResPieChart() { const $selection = d3.select(".res-svg"); $selection.selectAll("*").remove();
function renderPie(pie, newArc) { newArc .innerRadius(cDim.innerRadius) .outerRadius(cDim.outerRadius) .padAngle(0.1);
$selection
.attr("height", fixHeight)
.attr("width", width)
.append("g")
.attr("class", "pieChart")
.style("transform", "translate(75%, 55%)")
.selectAll("path")
.data(pie)
.enter()
.append("path")
.style("fill", (d, i) => {
return d3.schemeCategory10[(i + 9) % 10];
})
.style("stroke", "rgba(0,0,0,0)")
.style("stroke-width", "20px")
.transition()
.duration(1000)
.attrTween("d", function tweenDonut(b) {
b.innerRadius = 0;
let i = d3.interpolate(
{
startAngle: 0,
endAngle: 0
},
b
);
return function(t) {
return newArc(i(t));
};
});
}
function renderLabels(pie, arc, texts, percents) { let filterPie = pie, filterChoices = texts, filterRates = percents;
let labels = $selection
.select("g.pieChart")
.selectAll("text.label")
.data(filterPie);
labels
.enter()
.append("text")
.attr("class", "label")
.transition()
.duration(1000)
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".8em")
.attr("text-anchor", "middle")
.attr("fill", "white")
.text(function(d, i) {
return percents[i] ? texts[i] : "";
});
let percent = $selection
.select("g.pieChart")
.selectAll("text.pert")
.data(filterPie);
percent
.enter()
.append("text")
.attr("class", "pert")
.transition()
.duration(1000)
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", "0.01em")
.attr("text-anchor", "middle")
.attr("fill", "white")
.text(function(d, i) {
return filterRates[i] ? filterRates[i] * 100 + "%" : "";
});
}
let pieChart = d3.pie(); let surveyPie = pieChart(percents); let newArc = d3.arc();
renderPie(surveyPie, newArc); renderLabels(surveyPie, newArc, texts, percents); $selection.call(responsivefy); }
function createSurPieChart() { let surveyPie, svg = d3.select(".survey-svg"), canvas = svg.append("g").attr("id", "canvas"), art = canvas.append("g").attr("id", "art"), labels = canvas.append("g").attr("id", "labels");
surveyPie = d3.pie(); surveyPie.value(function(d, i) { return d; });
svg.attr("height", fixHeight); svg.attr("width", "100%"); canvas.style("transform", "translate(76%, 55%)");
const pied_data = surveyPie(numbers);
const pied_arc = d3 .arc() .innerRadius(cDim.innerRadius) .outerRadius(cDim.outerRadius) .padAngle(0.1);
const pied_colors = d3.schemeCategory10;
const enteringArcs = art .selectAll(".wedge") .data(pied_data) .enter();
enteringArcs .append("path") .attr("class", "wedge") // .attr("d", pied_arc) .style("fill", function(d, i) { return pied_colors[(i + 9) % 10]; }) .transition() .duration(1000) .attrTween("d", function tweenDonut(b) { b.innerRadius = 0; var i = d3.interpolate( { startAngle: 0, endAngle: 0 }, b ); return function(t) { return pied_arc(i(t)); }; });
const enteringLabels = labels .selectAll(".label") .data(pied_data) .enter(); const labelGroups = enteringLabels.append("g").attr("class", "label"); labelGroups .append("circle") .transition() .duration(1000) .attr("transform", function(d, i) { const centroid = pied_arc.centroid(d); return "translate(" + pied_arc.centroid(d) + ")"; }) .attr("class", "label-circle") .attr("dx", "0.57em");
const textLines = labelGroups .append("line") .transition() .duration(1000) .attr("x1", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) (cDim.labelRadius 0.7); return x; }) .attr("y1", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const y = Math.sin(midAngle) (cDim.labelRadius 0.7); return y; }) .attr("x2", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) (cDim.labelRadius 0.9); return x; }) .attr("y2", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const y = Math.sin(midAngle) (cDim.labelRadius 0.9); return y; }) .attr("class", "label-line") .attr("stroke", function(d, i) { return d.data ? d3.schemeCategory10[(i + 9) % 10] : ""; });
const textLabels = labelGroups .append("text") .attr("x", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) cDim.labelRadius; const sign = x > 0 ? 1 : -1; const labelX = x + 5 sign; return labelX 0.9; }) .attr("y", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const y = Math.sin(midAngle) cDim.labelRadius; return y 0.9; }) .attr("text-anchor", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) cDim.labelRadius; return x > 0 ? "start" : "end"; }) .text(function(d, i) { return d.data ? texts[i] : ""; }) .attr("class", "label-text") .attr("width", "70");
const percentLabels = labelGroups .append("text") .text(function(d, i) { return d.data ? percents[i] 100 + "%" : ""; }) .attr("fill", "white") .attr("width", "70") .transition() .duration(1000) .attr("x", function(d, i) { const centroid = pied_arc.centroid(d); return centroid[0]; }) .attr("y", function(d, i) { const centroid = pied_arc.centroid(d); return centroid[1]; }) .attr("text-anchor", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) cDim.labelRadius; // return (x > 0) ? "start" : "end"; return "middle"; });
const alpha = 0.5, spacing = 12;
function relax() { let again = false; textLabels.each(function(d, i) { let a = this, da = d3.select(a), y1 = da.attr("y"); textLabels.each(function(d, j) { let b = this; if (a == b) return; let db = d3.select(b); if (da.attr("text-anchor") != db.attr("text-anchor")) return; let y2 = db.attr("y"); let deltaY = y1 - y2; if (Math.abs(deltaY) > spacing) return; again = true; let sign = deltaY > 0 ? 1 : -1; const adjust = sign * alpha; da.attr("y", +y1 + adjust); db.attr("y", +y2 - adjust); }); });
if (again) {
let labelElements = textLabels._groups[0];
textLines.attr("y2", function(d, i) {
let labelForLine = d3.select(labelElements[i]);
return labelForLine.attr("y");
});
setTimeout(relax, 20);
}
}
relax(); textLabels.call(dotme); svg.call(responsivefy);
function dotme(text) { text.each(function() { var text = d3.select(this); var textbkp = text.text(); var words = text.text().split(""); var ellipsis = text .text("") .append("tspan") .attr("class", "elip") .text("...") .attr("font-size", "34"); var width = parseFloat(text.attr("width")) - ellipsis.node().getComputedTextLength(); var numWords = words.length;
var tspan = text
.insert("tspan", ":first-child")
.text(words.join(""))
.attr("font-size", "34");
while (tspan.node().getComputedTextLength() > width && words.length) {
tspan.text(
words
.join("")
.substr(0, width - 3)
.trim()
);
words.pop();
}
if (words.length === numWords) {
ellipsis.remove();
}
text
.append("title")
.attr("class", "tip")
.text(function(d, i) {
return textbkp;
})
.attr("font-size", "34")
.attr("fill", "black");
});
} }
function responsivefy(svg) { var container = d3.select(svg.node().parentNode), width = parseInt(svg.style("width")); // 1.5, height = parseInt(svg.style("height")); // 1.2, aspect = width / height; svg .attr("viewBox", "0 0 " + width + " " + height) .attr("perserveAspectRatio", "xMinYMid") .call(resize); d3.select(window).on("resize." + container.attr("id"), resize);
function resize() { var targetWidth = parseInt(container.style("width")); svg.attr("width", targetWidth); svg.attr("height", Math.round(targetWidth / aspect)); } }
createSurPieChart(); createResPieChart();
(function() { function wrapLines(text, width) { let textNode = document.getElementById("spanForSvg"); textNode.textContent = text; let textWidth = textNode.offsetWidth; return Math.ceil(textWidth / width) - 1; }
const sample2 = [ { language: "Rust is awesome for desktop app, you can try it, bang bang da", value: 78.9, color: "#000000" }, { language: "Kotlin is awesome for android app, you can try it, bang bang da yeah yeash yes perfect", value: 75.1, color: "#00a2ee" }, { language: "Python is awesome for nlp app, you can try it, bang bang da", value: 68.0, color: "#fbcb39" }, { language: "TypeScript is awesome for type js app, you can try it, bang bang da", value: 67.0, color: "#007bc8" }, { language: "Go is awesome for concurrent app, you can try it, bang bang da", value: 65.6, color: "#65cedb" }, { language: "Swift is awesome for ios app, you can try it, bang bang da, yeah yeash yes perfect", value: 65.1, color: "#ff6e52" }, { language: "JavaScript is awesome for universal app, you can try it, bang bang da yeah yeash yes perfect", value: 61.9, color: "#f9de3f" }, { language: "C# is awesome for web app, you can try it, bang bang da yeah yeash yes perfect", value: 60.4, color: "#5d2f8e" }, { language: "F# is awesome for desktop app, you can try it, bang bang da yeah yeash yes perfect", value: 59.6, color: "#008fc9" }, { language: "Clojure is awesome for web app, you can try it, bang bang da yeah yeash yes perfect", value: 59.6, color: "#507dca" } ];
const sample = [ { language: "Rust is awesome fit, bang bang da", value: 78.9, color: "#000000" }, { language: "Kotlin is awesome for android app,t", value: 75.1, color: "#00a2ee" }, { language: "Python is awesome", value: 68.0, color: "#fbcb39" }, { language: "TypeScript is awesome for", value: 67.0, color: "#007bc8" }, { language: "Go is awesome for cong bang da", value: 65.6, color: "#65cedb" }, { language: "Swift is awesome, yeah yeash yes perfect", value: 65.1, color: "#ff6e52" }, { language: "JavaScript is awesome for u yeah yeash yes perfect", value: 61.9, color: "#f9de3f" }, { language: "C# is awesome for weerfect", value: 60.4, color: "#5d2f8e" }, { language: "F# is awesome for desktop app, t", value: 59.6, color: "#008fc9" }, { language: "Clojure is awesome for web app, you can try", value: 59.6, color: "#507dca" } ]; const svg = d3.select(".list #container svg"); const svgContainer = d3.select("#container");
const margin = 20; const width = window.innerWidth / 3; const lineNumsForText = sample .map(s => s.language) .map(t => getWraps(t, width - 6 * margin )); console.log("lineNumsForText ", lineNumsForText);
svg.attr("width", width - 2 * margin);
const height = 54 * sample.length;
const chart = svg
.append("g")
.attr("transform", translate(${margin}, ${margin})
);
const yScale = d3 .scaleBand() .range([height, 0]) .domain(sample.map(s => s.language)) .padding(0.6);
svg.style('height', height + yScale.bandwidth() * lineNumsForText.reduce((i, s) => s + i, 0))
const xScale = d3 .scaleLinear() .range([0, width - 2 * margin]) .domain([0, 100]);
const barGroups = chart .selectAll() .data(sample) .enter() .append("g");
barGroups .append("rect") .attr("class", "back-bar") .attr("x", g => 0) .attr("y", (g, i) => { let nums = 0; for (let j = 0; j <= i; j++) { nums += lineNumsForText[j]; } // return yScale(g.language) - nums yScale.bandwidth(); return ( height - yScale(g.language) + nums yScale.bandwidth() - yScale.bandwidth() ); }) .attr("width", g => width - 2 * margin) .attr("height", yScale.bandwidth());
barGroups .append("rect") .attr("class", "bar") .attr("x", g => 0) .attr("y", (g, i) => { let nums = 0; for (let j = 0; j <= i; j++) { nums += lineNumsForText[j]; } // return yScale(g.language) - nums yScale.bandwidth(); return ( height - yScale(g.language) + nums yScale.bandwidth() - yScale.bandwidth() ); }) .attr("width", g => xScale(g.value) - 2 * margin) .attr("height", yScale.bandwidth());
barGroups
.append("text")
.attr("class", "value")
.attr("y", (g, i) => {
let nums = 0;
for (let j = 0; j <= i; j++) {
nums += lineNumsForText[j];
}
// (a) => height - yScale(a.language) - yScale.bandwidth() 1.2 )
return (
height -
yScale(g.language) +
nums yScale.bandwidth() -
(lineNumsForText[i] + 1) * Math.ceil(yScale.bandwidth())
);
})
.attr("x", a => 0)
.attr("text-anchor", "start")
.text(a => ${a.value}%
);
const languages = barGroups
.append("text")
.attr("class", "label")
.attr("y", (g, i) => {
let nums = 0;
for (let j = 0; j <= i; j++) {
nums += lineNumsForText[j];
}
// (a) => height - yScale(a.language) - yScale.bandwidth() 1.2 )
return (
height -
yScale(g.language) +
nums yScale.bandwidth() -
(lineNumsForText[i] +1) Math.ceil(yScale.bandwidth()) - (lineNumsForText[i] === 0 ? 4 : 0)
);
})
.attr("x", a => 0 + 60)
.attr("text-anchor", "start")
.text(a => ${a.language}
);
languages.call(wrap, width - margin 6);
const barTextGroups = chart .selectAll() .data(sample) .enter() .append("g");
barTextGroups
.append("text")
.attr("class", "pp-value")
.attr("y", (g, i) => {
let nums = 0;
for (let j = 0; j <= i; j++) {
nums += lineNumsForText[j];
}
// return yScale(g.language) - nums yScale.bandwidth();
return (
height -
yScale(g.language) +
nums yScale.bandwidth() - 6
);
})
.attr("x", a => 10)
.attr("fill", "red")
.attr("text-anchor", "start")
.text(a => ${Math.ceil(Math.random() * 100)}
);
// svg.call(responsivefy); })();
function getWraps(text, width) { let textNode = document.getElementById("spanForSvg"); let words = text.split(/\s+/).reverse(), word, line = [], lineNumber = 0; while ((word = words.pop())) { line.push(word); textNode.textContent = line.join(" "); let textWidth = textNode.offsetWidth; if ( textWidth > width) { line.pop(); line = [word]; lineNumber++; } } return lineNumber; }
function wrap(text, width) { text.each(function() { var text = d3.select(this), words = text .text() .split(/\s+/) .reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy")) || 1.2, tspan = text .text(null) .append("tspan") .attr("x", 60) .attr("y", y); while ((word = words.pop())) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text .append("tspan") .attr("x", 60) .attr("y", y) .attr("dy", ++lineNumber * lineHeight + "em") .text(word); } } }); }
<!DOCTYPE html>
let percents = [0.1, 0.2, 0.7], texts = ["aaaa", "bbb", "ccc"], numbers = [10, 20, 70], totalCount = 100; const isCrossMinWidth = window.innerWidth <= 750; const fixHeight = 500; let width = isCrossMinWidth ? window.innerWidth : window.innerWidth / 3; let height = isCrossMinWidth ? window.innerHeight : window.innerHeight / 3;
let cDim = { height: height, width: width, innerRadius: width 0.1, outerRadius: width 0.4, labelRadius: width * 0.6 };
function createResPieChart() { const $selection = d3.select(".res-svg"); $selection.selectAll("*").remove();
function renderPie(pie, newArc) { newArc .innerRadius(cDim.innerRadius) .outerRadius(cDim.outerRadius) .padAngle(0.1);
}
function renderLabels(pie, arc, texts, percents) { let filterPie = pie, filterChoices = texts, filterRates = percents;
}
let pieChart = d3.pie(); let surveyPie = pieChart(percents); let newArc = d3.arc();
renderPie(surveyPie, newArc); renderLabels(surveyPie, newArc, texts, percents); $selection.call(responsivefy); }
function createSurPieChart() { let surveyPie, svg = d3.select(".survey-svg"), canvas = svg.append("g").attr("id", "canvas"), art = canvas.append("g").attr("id", "art"), labels = canvas.append("g").attr("id", "labels");
surveyPie = d3.pie(); surveyPie.value(function(d, i) { return d; });
svg.attr("height", fixHeight); svg.attr("width", "100%"); canvas.style("transform", "translate(76%, 55%)");
const pied_data = surveyPie(numbers);
const pied_arc = d3 .arc() .innerRadius(cDim.innerRadius) .outerRadius(cDim.outerRadius) .padAngle(0.1);
const pied_colors = d3.schemeCategory10;
const enteringArcs = art .selectAll(".wedge") .data(pied_data) .enter();
enteringArcs .append("path") .attr("class", "wedge") // .attr("d", pied_arc) .style("fill", function(d, i) { return pied_colors[(i + 9) % 10]; }) .transition() .duration(1000) .attrTween("d", function tweenDonut(b) { b.innerRadius = 0; var i = d3.interpolate( { startAngle: 0, endAngle: 0 }, b ); return function(t) { return pied_arc(i(t)); }; });
const enteringLabels = labels .selectAll(".label") .data(pied_data) .enter(); const labelGroups = enteringLabels.append("g").attr("class", "label"); labelGroups .append("circle") .transition() .duration(1000) .attr("transform", function(d, i) { const centroid = pied_arc.centroid(d); return "translate(" + pied_arc.centroid(d) + ")"; }) .attr("class", "label-circle") .attr("dx", "0.57em");
const textLines = labelGroups .append("line") .transition() .duration(1000) .attr("x1", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) (cDim.labelRadius 0.7); return x; }) .attr("y1", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const y = Math.sin(midAngle) (cDim.labelRadius 0.7); return y; }) .attr("x2", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) (cDim.labelRadius 0.9); return x; }) .attr("y2", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const y = Math.sin(midAngle) (cDim.labelRadius 0.9); return y; }) .attr("class", "label-line") .attr("stroke", function(d, i) { return d.data ? d3.schemeCategory10[(i + 9) % 10] : ""; });
const textLabels = labelGroups .append("text") .attr("x", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) cDim.labelRadius; const sign = x > 0 ? 1 : -1; const labelX = x + 5 sign; return labelX 0.9; }) .attr("y", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const y = Math.sin(midAngle) cDim.labelRadius; return y 0.9; }) .attr("text-anchor", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) cDim.labelRadius; return x > 0 ? "start" : "end"; }) .text(function(d, i) { return d.data ? texts[i] : ""; }) .attr("class", "label-text") .attr("width", "70");
const percentLabels = labelGroups .append("text") .text(function(d, i) { return d.data ? percents[i] 100 + "%" : ""; }) .attr("fill", "white") .attr("width", "70") .transition() .duration(1000) .attr("x", function(d, i) { const centroid = pied_arc.centroid(d); return centroid[0]; }) .attr("y", function(d, i) { const centroid = pied_arc.centroid(d); return centroid[1]; }) .attr("text-anchor", function(d, i) { const centroid = pied_arc.centroid(d); const midAngle = Math.atan2(centroid[1], centroid[0]); const x = Math.cos(midAngle) cDim.labelRadius; // return (x > 0) ? "start" : "end"; return "middle"; });
const alpha = 0.5, spacing = 12;
function relax() { let again = false; textLabels.each(function(d, i) { let a = this, da = d3.select(a), y1 = da.attr("y"); textLabels.each(function(d, j) { let b = this; if (a == b) return; let db = d3.select(b); if (da.attr("text-anchor") != db.attr("text-anchor")) return; let y2 = db.attr("y"); let deltaY = y1 - y2; if (Math.abs(deltaY) > spacing) return; again = true; let sign = deltaY > 0 ? 1 : -1; const adjust = sign * alpha; da.attr("y", +y1 + adjust); db.attr("y", +y2 - adjust); }); });
}
relax(); textLabels.call(dotme); svg.call(responsivefy);
function dotme(text) { text.each(function() { var text = d3.select(this); var textbkp = text.text(); var words = text.text().split(""); var ellipsis = text .text("") .append("tspan") .attr("class", "elip") .text("...") .attr("font-size", "34"); var width = parseFloat(text.attr("width")) - ellipsis.node().getComputedTextLength(); var numWords = words.length;
} }
function responsivefy(svg) { var container = d3.select(svg.node().parentNode), width = parseInt(svg.style("width"))// 1.5, height = parseInt(svg.style("height"))// 1.2, aspect = width / height; svg .attr("viewBox", "0 0 " + width + " " + height) .attr("perserveAspectRatio", "xMinYMid") .call(resize); d3.select(window).on("resize." + container.attr("id"), resize);
function resize() { var targetWidth = parseInt(container.style("width")); svg.attr("width", targetWidth); svg.attr("height", Math.round(targetWidth / aspect)); } }
createSurPieChart(); createResPieChart();
(function () { const sample = [ { language: 'Rust is awesome for desktop app, you can try it, bang bang da', value: 78.9, color: '#000000' }, { language: 'Kotlin is awesome for android app, you can try it, bang bang da yeah yeash yes perfect', value: 75.1, color: '#00a2ee' }, { language: 'Python is awesome for nlp app, you can try it, bang bang da', value: 68.0, color: '#fbcb39' }, { language: 'TypeScript is awesome for type js app, you can try it, bang bang da', value: 67.0, color: '#007bc8' }, { language: 'Go is awesome for concurrent app, you can try it, bang bang da', value: 65.6, color: '#65cedb' }, { language: 'Swift is awesome for ios app, you can try it, bang bang da, yeah yeash yes perfect', value: 65.1, color: '#ff6e52' }, { language: 'JavaScript is awesome for universal app, you can try it, bang bang da yeah yeash yes perfect', value: 61.9, color: '#f9de3f' }, { language: 'C# is awesome for web app, you can try it, bang bang da yeah yeash yes perfect', value: 60.4, color: '#5d2f8e' }, { language: 'F# is awesome for desktop app, you can try it, bang bang da yeah yeash yes perfect', value: 59.6, color: '#008fc9' }, { language: 'Clojure is awesome for web app, you can try it, bang bang da yeah yeash yes perfect', value: 59.6, color: '#507dca' } ];
const svg = d3.select('.list #container svg'); const svgContainer = d3.select('#container');
const margin = 20; const width = window.innerWidth / 3; const height = 54 * sample.length;
const chart = svg.append('g') .attr('transform',
translate(${margin}, ${margin})
);const yScale = d3.scaleBand() .range([height, 0]) .domain(sample.map((s) => s.language)) .padding(0.6)
const xScale = d3.scaleLinear() .range([0, width]) .domain([0, 100]);
const barGroups = chart.selectAll() .data(sample) .enter() .append('g')
barGroups .append('rect') .attr('class', 'bar') .attr('x', (g) => 0) .attr('y', (g) => yScale(g.language)) .attr('width', (g) => width - xScale(g.value)-2*margin) .attr('height', yScale.bandwidth())
barGroups .append('text') .attr('class', 'value') .attr('y', (a) => height - yScale(a.language) - yScale.bandwidth() * 1.2 ) .attr('x', (a) => 0 ) .attr('text-anchor', 'start') .text((a) =>
${a.value}%
)const languages = barGroups .append('text') .attr('class', 'label') .attr('y', (a) => height - yScale(a.language) - yScale.bandwidth() 1.2 ) .attr('x', (a) => 0 + 60) .attr('text-anchor', 'start') .text((a) =>
${a.language}
) languages.call(wrap, width - margin 5) const barTextGroups = chart.selectAll() .data(sample) .enter() .append('g')}())
function wrap(text, width) { text.each(function() { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy")) || 1.2, tspan = text.text(null).append("tspan").attr("x", 60).attr("y", y); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan").attr("x", 60).attr("y", y).attr("dy", ++lineNumber * lineHeight + "em").text(word); } } }); }