Published
Edited
Aug 23, 2021
3 stars
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
htmlCanvas = html`<canvas id="output"></canvas>`
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
imageAspect = image.height / image.width
Insert cell
displayWidth = canvasWidth - margin.left - margin.right
Insert cell
canvasHeight = Math.ceil(canvasWidth * imageAspect + margin.bottom)
Insert cell
outputWidth = canvasWidth * scale
Insert cell
outputHeight = canvasHeight * scale
Insert cell
cutHeight = displayWidth * imageAspect - margin.top
Insert cell
backgroundColor = d3.color(colorString)
Insert cell
// this is base64 representation of the output image, we create this to make it easier to download the image
mutable imageDataUrl = htmlCanvas.toDataURL('png')
Insert cell
// replace the height vertical line in the chosen path with a distance calculated from the size of the image
clipPath = edge.path.replace(/v\d*\.\d*c/, `v${cutHeight}`)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
svg64 = import('https://cdn.skypack.dev/svg64@1.1.0?min')
Insert cell
Insert cell
svg = html`<svg id="preview" class="image" width="${outputWidth}" viewbox="0.000001 0 ${canvasWidth} ${canvasHeight}">
<defs>
<filter id="dropshadow">
<feGaussianBlur in="FillPaint" stdDeviation="8"></feGaussianBlur>
<feColorMatrix type="matrix"
values="${backgroundColor.r / 255} 0 0 0 0
0 ${backgroundColor.g / 255} 0 0 0
0 0 ${backgroundColor.b / 255} 0 0
0 0 0 0.4 0" />
</filter>
<clipPath id="edge" clipPathUnits="userSpaceOnUse">
<path
d="${clipPath}" style="transform: scaleX(${canvasWidth/620})">
</path>
</clipPath>
</defs>
<g class="inner">
<rect width="${canvasWidth}" height="${canvasHeight}" style="fill: ${colorString}"></rect>
<path fill="rgba(0, 0, 0, 0.2)" filter="url(#dropshadow)" d="${clipPath}" style="transform: scaleX(${canvasWidth/620})"></path>
<image x="${margin.left}" href="${base64image}" width="${displayWidth}" height="${displayWidth * imageAspect}" clip-path="url(#edge)"></image>
<text class="source" x="${canvasWidth - margin.right}" y="${canvasHeight}" dy="-6" style="text-anchor: end; font-size:14px; fill: rgb(150,150,150)">${credit}</text>
</g>
</svg>`
Insert cell
// {
// return html`<div id="svg-wrap" style="max-width:100%">
// ${svg}
// </div>`
// }
Insert cell
Insert cell
html`
<style type="text/css">
#output {
max-width: 100%;
height:auto !important;
}
#svg-wrap svg {
width: 100%;
}
</style>
`
Insert cell
Insert cell
image = html`<img src=${base64image}>`
Insert cell
// this isn't currently working right, there is usually a way to add a credit line into the exported image
credit = ""
Insert cell
Insert cell
svgimg = html`<img src=${svg64.default(svg)}>`
Insert cell
Insert cell
// this is run every 0.5 seconds to make sure that the canvas draw doesn't get reset by other bits of the code executing after or prior to the draw.

// create a new Promise every 0.5 seconds that returns the drawing function
Promises.tick(500, _ => {
htmlCanvas.width = outputWidth
htmlCanvas.height = outputHeight
let ctx = htmlCanvas.getContext("2d")
ctx.drawImage(svgimg, 0, 0, outputWidth, outputHeight)
mutable imageDataUrl = htmlCanvas.toDataURL('png')
}).then(draw => draw()) //when the promise returns the function, call it
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