<div id="ccc"></div>
<div id="config">
<div class="config-wrap">
<label
id="num-particles-label"
for="num-particles"
>
Num Particles (200)
</label>
<input
type="range"
min="50"
max="400"
id="num-particles"
name="num-particles"
value="200"
/>
</div>
<div class="config-wrap">
<label
id="line-width-label"
for="line-width"
>
Line width (1)
</label>
<input
type="range"
min="1"
max="20"
id="line-width"
name="line-width"
value="1"
/>
</div>
<div class="config-wrap">
<label
id="min-dist-label"
for="min-dist"
>
Min dist (50)
</label>
<input
type="range"
min="1"
max="100"
step="1"
id="min-dist"
name="min-dist"
value="50"
/>
</div>
<div class="config-wrap">
<label
id="stroke-color-label"
for="stroke-color"
>
Stroke Color (#2980b9)
</label>
<input
type="color"
min="1"
max="20"
step="1"
id="stroke-color"
name="stroke-color"
value="#2980b9"
/>
</div>
<div class="config-wrap">
<label
id="fill-color-label"
for="fill-color"
>
Fill Color (#2980b9)
</label>
<input
type="color"
min="1"
max="20"
step="1"
id="fill-color"
name="fill-color"
value="#2980b9"
/>
</div>
</div>
<script id="c" type="text/worklet">
const PAINTLET_NAME = 'dots-connections'
class CSSPaintlet {
static get inputProperties() {
return [
`--${PAINTLET_NAME}-line-width`,
`--${PAINTLET_NAME}-stroke-color`,
`--${PAINTLET_NAME}-fill-color`,
`--${PAINTLET_NAME}-connection-min-dist`,
`--${PAINTLET_NAME}-count`,
]
}
paint(ctx, paintSize, props, args) {
const lineWidth = Number(props.get(`--${PAINTLET_NAME}-line-width`))
const minDist = Number(props.get(`--${PAINTLET_NAME}-connection-min-dist`))
const strokeColor = props.get(`--${PAINTLET_NAME}-stroke-color`)
const fillColor = props.get(`--${PAINTLET_NAME}-fill-color`)
const numParticles = Number(props.get(`--${PAINTLET_NAME}-count`))
console.log(props)
const particles = new Array(numParticles).fill(null).map(_ => ({
x: Math.random() * paintSize.width,
y: Math.random() * paintSize.height,
radius: 2 + Math.random() * 2,
}))
ctx.lineWidth = lineWidth
ctx.lineJoin = 'round'
ctx.lineCap = 'round'
for (let i = 0; i < numParticles; i++) {
const particle = particles[i]
for (let n = 0; n < numParticles; n++) {
if (i === n) {
continue
}
const nextParticle = particles[n]
const dx = nextParticle.x - particle.x
const dy = nextParticle.y - particle.y
const dist = Math.sqrt(dx * dx + dy * dy)
if (dist < minDist) {
ctx.strokeStyle = strokeColor
ctx.beginPath()
ctx.moveTo(nextParticle.x, nextParticle.y)
ctx.lineTo(particle.x, particle.y)
ctx.stroke()
}
}
ctx.fillStyle = fillColor
ctx.beginPath()
ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2)
ctx.closePath()
ctx.fill()
}
}
}
registerPaint(PAINTLET_NAME, CSSPaintlet)
</script>
* {
margin: 0;
padding: 0;
font-family: Helvetica, sans-serif;
}
#ccc {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
--dots-connections-line-width: 1;
--dots-connections-stroke-color: #2980b9;
--dots-connections-fill-color: #2980b9;
--dots-connections-connection-min-dist: 50;
--dots-connections-count: 200;
background-image: paint(dots-connections);
background-repeat: no-repeat;
background-size: cover;
}
#config {
position: fixed;
bottom: 0;
left: 0;
padding: 1rem;
background: rgba(255, 255, 255, 0.8);
backdrop-filter: blur(3px);
box-sizing: border-box;
border-top-right-radius: 24px;
}
.config-wrap {
margin-bottom: 0.75rem;
color: rgba(0, 0, 0, 0.8);
}
label {
display: block;
font-size: 11px;
text-transform: uppercase;
margin-bottom: 0.5rem;
}
console.clear()
const paintletCode = document.getElementById('c')
;(async function() {
// ⚠️ Handle Firefox and Safari by importing a polyfill for CSS Pain
if (CSS['paintWorklet'] === undefined) {
await import('https://unpkg.com/css-paint-polyfill')
}
// Explicitly define our custom CSS variable
// Make sure that the browser treats it as a number
// It does not inherit it's value
// It's initial value defaults to 1
if ('registerProperty' in CSS) {
CSS.registerProperty({
name: '--dots-connections-line-width',
syntax: '<number>',
inherits: false,
initialValue: 10
})
CSS.registerProperty({
name: '--dots-connections-stroke-color',
syntax: '<color>',
inherits: false,
initialValue: '#2980b9'
})
CSS.registerProperty({
name: '--dots-connections-fill-color',
syntax: '<color>',
inherits: false,
initialValue: '#2980b9'
})
CSS.registerProperty({
name: '--dots-connections-connection-min-dist',
syntax: '<number>',
inherits: false,
initialValue: 50
})
}
const numParticlesInput = document.getElementById('num-particles')
const lineWidthInput = document.getElementById('line-width')
const minDistInput = document.getElementById('min-dist')
const strokeInput = document.getElementById('stroke-color')
const fillInput = document.getElementById('fill-color')
const numParticlesLabel = document.getElementById('num-particles-label')
const lineWidthLabel = document.getElementById('line-width-label')
const minDistLabel = document.getElementById('min-dist-label')
const strokeLabel = document.getElementById('stroke-color-label')
const fillLabel = document.getElementById('fill-color-label')
const workletElement = document.getElementById('ccc')
numParticlesInput.addEventListener('change', e => {
const numParticles = Number(e.target.value)
numParticlesLabel.textContent = `Num Particles (${numParticles})`
workletElement.style.setProperty('--dots-connections-count', numParticles)
})
lineWidthInput.addEventListener('change', e => {
const value = Number(e.target.value)
lineWidthLabel.textContent = `Line width (${value})`
workletElement.style.setProperty('--dots-connections-line-width', value)
})
minDistInput.addEventListener('change', e => {
const value = Number(e.target.value)
minDistLabel.textContent = `Min Dist (${value})`
workletElement.style.setProperty('--dots-connections-connection-min-dist', value)
})
strokeInput.addEventListener('change', e => {
strokeLabel.textContent = `Stroke Color (${e.target.value})`
workletElement.style.setProperty('--dots-connections-stroke-color', e.target.value)
})
fillInput.addEventListener('change', e => {
fillLabel.textContent = `Fill Color (${e.target.value})`
workletElement.style.setProperty('--dots-connections-fill-color', e.target.value)
})
// Include our paintlet using
registerCSSPaintlet(paintletCode.textContent)
})()
function registerCSSPaintlet (sourceCode) { CSS.paintWorklet.addModule(`data:application/javascript;charset=utf8,${encodeURIComponent(sourceCode)}`)
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.