Я добавил это в мой html контейнер:
<canvas width="16" height="9" style="width: 100%"></canvas>
и это в мой сценарий d3:
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 980 550")
Вы можете сообщить мне, если есть лучший способ сделай это. Я не могу использовать window.Innerwidth из-за ограничений страницы, на которой я должен добавить это.
HTML FILE:
<div id="wrapper" class="wrapper">
<canvas width="16" height="9" style="width: 100%"></canvas>
<div id="tooltip" class="tooltip">
<div class="tooltip-date">
<span id="date"></span>
</div>
<div class="tooltip-confirmed">
Confirmed Cases: <span id="confirmed"></span>
</div>
</div>
</div>
CSS:
.wrapper {
position: relative;
}
.line {
fill: none;
stroke: #FF9933;
stroke-width: 2;
}
.y-axis-label {
fill: black;
font-size: 1.4em;
text-anchor: middle;
transform: rotate(-90deg);
}
.listening-rect {
fill: transparent;
}
body {
display: flex;
justify-content: center;
padding: 5em 1em;
font-family: sans-serif;
}
.tooltip {
opacity: 0;
position: absolute;
top: -14px;
left: 0;
padding: 0.6em 1em;
background: #fff;
text-align: center;
line-height: 1.4em;
font-size: 0.9em;
border: 1px solid #ddd;
z-index: 10;
transition: all 0.1s ease-out;
pointer-events: none;
}
.tooltip:before {
content: '';
position: absolute;
bottom: 0;
left: 50%;
width: 12px;
height: 12px;
background: white;
border: 1px solid #ddd;
border-top-color: transparent;
border-left-color: transparent;
transform: translate(-50%, 50%) rotate(45deg);
transform-origin: center center;
z-index: 10;
}
.tooltip-date {
margin-bottom: 0.2em;
font-weight: 600;
font-size: 1.1em;
line-height: 1.4em;
}
Сценарий D3:
async function drawLineChart() {
let dataset = await d3.json('./history.json')
yAccessor = d => d.summary.total
const dateParser = d3.timeParse("%Y-%m-%d")
format = d3.timeFormat("%b-%d")
const xAccessor = d => dateParser(d.day)
console.log(format(xAccessor(dataset[0])))
dataset = dataset.sort((a,b) => xAccessor(a) - xAccessor(b)).slice(0, 100)
let dimensions = {
width: 980,
height: 550,
margin: {
top: 20,
right: 30,
bottom: 30,
left: 70,
},
}
dimensions.boundedWidth = dimensions.width - dimensions.margin.left - dimensions.margin.right
dimensions.boundedHeight = dimensions.height - dimensions.margin.top - dimensions.margin.bottom
// 3. Draw canvas
const wrapper = d3.select("#wrapper")
.append("svg")
.attr("preserveAspectRatio", "xMinYMin meet")
.attr("viewBox", "0 0 980 550")
// .attr("width", dimensions.width)
// .attr("height", dimensions.height)
wrapper.append("rect")
.attr("class", "background")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#faebd7")
const bounds = wrapper.append("g")
.attr("transform", `translate(${
dimensions.margin.left
}, ${
dimensions.margin.top
})`)
bounds.append("text")
.attr("class", "watermark")
.attr("x", dimensions.width - 180)
.attr("y", dimensions.height - 60)
.attr("fill", "grey")
.style("opacity", 0.5)
.html("@Alepthoughts")
bounds.append("defs").append("clipPath")
.attr("id", "bounds-clip-path")
.append("rect")
.attr("width", dimensions.boundedWidth)
.attr("height", dimensions.boundedHeight)
const clip = bounds.append("g")
.attr("clip-path", "url(#bounds-clip-path)")
// 4. Create scales
const yScale = d3.scaleLinear()
.domain(d3.extent(dataset, yAccessor))
.range([dimensions.boundedHeight, 0])
.nice()
const xScale = d3.scaleTime()
.domain(d3.extent(dataset, xAccessor))
.range([0, dimensions.boundedWidth])
const lineGenerator = d3.line()
.x(d => xScale(xAccessor(d)))
.y(d => yScale(yAccessor(d)))
const line = clip.append("path")
.attr("class", "line")
.attr("d", lineGenerator(dataset))
const yAxisGenerator = d3.axisLeft()
.scale(yScale)
const yAxis = bounds.append("g")
.attr("class", "y-axis")
.call(yAxisGenerator)
const yAxisLabel = yAxis.append("text")
.attr("class", "y-axis-label")
.attr("x", -dimensions.boundedHeight / 2)
.attr("y", -dimensions.margin.left + 20)
.html("Confirmed Cases")
const listeningRect = bounds.append("rect")
.attr("class", "listening-rect")
.attr("width", dimensions.boundedWidth)
.attr("height", dimensions.boundedHeight)
.on("mousemove", onMouseMove)
.on("mouseleave", onMouseLeave)
const tooltip = d3.select("#tooltip")
const tooltipCircle = bounds.append("circle")
.attr("class", "tooltip-circle")
.attr("r", 4)
.attr("stroke", "#FF9933")
.attr("fill", "white")
.attr("stroke-width", 2)
.style("opacity", 0)
function onMouseMove() {
const mousePosition = d3.mouse(this)
const hoveredDate = xScale.invert(mousePosition[0])
const getDistanceFromHoveredDate = d => Math.abs(xAccessor(d) - hoveredDate)
const closestIndex = d3.scan(dataset, (a, b) => (
getDistanceFromHoveredDate(a) - getDistanceFromHoveredDate(b)
))
const closestDataPoint = dataset[closestIndex]
const closestXValue = xAccessor(closestDataPoint)
const closestYValue = yAccessor(closestDataPoint)
const formatDate = d3.timeFormat("%B %A %-d, %Y")
tooltip.select("#date")
.text(formatDate(closestXValue))
//const formatTemperature = d => `${d3.format(".1f")(d)}°F`
tooltip.select("#confirmed")
.html(closestYValue)
const x = xScale(closestXValue)
+ dimensions.margin.left
const y = yScale(closestYValue)
+ dimensions.margin.top
tooltip.style("transform", `translate(`
+ `calc(-50% + ${x}px),`
+ `calc(-100% + ${y}px)`
+ `)`)
tooltip.style("opacity", 1)
tooltipCircle
.attr("cx", xScale(closestXValue))
.attr("cy", yScale(closestYValue))
.style("opacity", 1)
}
function onMouseLeave() {
tooltip.style("opacity", 0)
tooltipCircle.style("opacity", 0)
}
}
Пример формата данных:
[
{
"day": "2019-03-10",
"summary": {
"total": 47,
},
{
"day": "2019-03-11",
"summary": {
"total": 60,
}
]