2021-07-16 16:47:18 +00:00
|
|
|
import React, { useRef, useEffect, useContext } from 'react'
|
|
|
|
import { randInRange } from '../lib/rand'
|
2021-04-22 22:14:32 +00:00
|
|
|
|
2021-07-16 16:47:18 +00:00
|
|
|
export const LightningContext = React.createContext({
|
|
|
|
bolts: 0,
|
|
|
|
strike: () => {}
|
|
|
|
})
|
2021-04-22 22:14:32 +00:00
|
|
|
|
|
|
|
export class LightningProvider extends React.Component {
|
|
|
|
state = {
|
|
|
|
bolts: 0,
|
2021-07-16 16:47:18 +00:00
|
|
|
strike: (repeat) => {
|
|
|
|
this.setState(state => {
|
|
|
|
return {
|
|
|
|
...this.state,
|
|
|
|
bolts: this.state.bolts + 1
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2021-04-22 22:14:32 +00:00
|
|
|
}
|
2021-04-20 00:47:40 +00:00
|
|
|
|
2021-04-22 22:14:32 +00:00
|
|
|
render () {
|
|
|
|
const { state, props: { children } } = this
|
|
|
|
return (
|
|
|
|
<LightningContext.Provider value={state}>
|
|
|
|
{new Array(this.state.bolts).fill(null).map((_, i) => <Lightning key={i} />)}
|
|
|
|
{children}
|
|
|
|
</LightningContext.Provider>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export const LightningConsumer = LightningContext.Consumer
|
2021-07-16 16:47:18 +00:00
|
|
|
export function useLightning () {
|
|
|
|
const { strike } = useContext(LightningContext)
|
|
|
|
return strike
|
|
|
|
}
|
2021-04-20 00:47:40 +00:00
|
|
|
|
|
|
|
export function Lightning () {
|
|
|
|
const canvasRef = useRef(null)
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
const canvas = canvasRef.current
|
|
|
|
const context = canvas.getContext('2d')
|
|
|
|
|
|
|
|
canvas.width = window.innerWidth
|
|
|
|
canvas.height = window.innerHeight
|
|
|
|
|
|
|
|
const bolt = new Bolt(context, {
|
|
|
|
startPoint: [Math.random() * (canvas.width * 0.5) + (canvas.width * 0.25), 0],
|
|
|
|
length: canvas.height,
|
2021-04-22 22:14:32 +00:00
|
|
|
speed: 100,
|
|
|
|
spread: 30,
|
|
|
|
branches: 20
|
2021-04-20 00:47:40 +00:00
|
|
|
})
|
|
|
|
bolt.draw()
|
|
|
|
}, [])
|
|
|
|
|
2021-04-22 22:14:32 +00:00
|
|
|
return <canvas className='position-fixed' ref={canvasRef} style={{ zIndex: -1 }} />
|
2021-04-20 00:47:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function Bolt (ctx, options) {
|
|
|
|
this.options = {
|
|
|
|
startPoint: [0, 0],
|
|
|
|
length: 100,
|
|
|
|
angle: 90,
|
|
|
|
speed: 30,
|
|
|
|
spread: 50,
|
|
|
|
branches: 10,
|
|
|
|
maxBranches: 10,
|
2021-04-22 22:14:32 +00:00
|
|
|
lineWidth: 3,
|
2021-04-20 00:47:40 +00:00
|
|
|
...options
|
|
|
|
}
|
|
|
|
this.point = [this.options.startPoint[0], this.options.startPoint[1]]
|
|
|
|
this.branches = []
|
|
|
|
this.lastAngle = this.options.angle
|
|
|
|
this.children = []
|
|
|
|
|
|
|
|
const radians = this.options.angle * Math.PI / 180
|
|
|
|
this.endPoint = [
|
|
|
|
this.options.startPoint[0] + Math.cos(radians) * this.options.length,
|
|
|
|
this.options.startPoint[1] + Math.sin(radians) * this.options.length
|
|
|
|
]
|
|
|
|
|
|
|
|
ctx.shadowColor = 'rgba(250, 218, 94, 1)'
|
|
|
|
ctx.shadowBlur = 5
|
|
|
|
ctx.shadowOffsetX = 0
|
|
|
|
ctx.shadowOffsetY = 0
|
|
|
|
ctx.fillStyle = 'rgba(250, 250, 250, 1)'
|
|
|
|
ctx.strokeStyle = 'rgba(250, 218, 94, 1)'
|
2021-04-22 22:14:32 +00:00
|
|
|
ctx.lineWidth = this.options.lineWidth
|
2021-04-20 00:47:40 +00:00
|
|
|
this.draw = (isChild) => {
|
|
|
|
ctx.beginPath()
|
|
|
|
ctx.moveTo(this.point[0], this.point[1])
|
2021-07-16 16:47:18 +00:00
|
|
|
const angleChange = randInRange(1, this.options.spread)
|
2021-04-20 00:47:40 +00:00
|
|
|
this.lastAngle += this.lastAngle > this.options.angle ? -angleChange : angleChange
|
|
|
|
const radians = this.lastAngle * Math.PI / 180
|
|
|
|
|
|
|
|
this.point[0] += Math.cos(radians) * this.options.speed
|
|
|
|
this.point[1] += Math.sin(radians) * this.options.speed
|
|
|
|
|
|
|
|
ctx.lineTo(this.point[0], this.point[1])
|
|
|
|
ctx.stroke()
|
|
|
|
|
|
|
|
const d = Math.sqrt(
|
|
|
|
Math.pow(this.point[0] - this.options.startPoint[0], 2) +
|
|
|
|
Math.pow(this.point[1] - this.options.startPoint[1], 2)
|
|
|
|
)
|
|
|
|
|
2021-04-22 22:14:32 +00:00
|
|
|
// make skinnier?
|
|
|
|
// ctx.lineWidth = ctx.lineWidth * 0.98
|
|
|
|
|
2021-07-16 16:47:18 +00:00
|
|
|
if (randInRange(0, 99) < this.options.branches && this.children.length < this.options.maxBranches) {
|
2021-04-20 00:47:40 +00:00
|
|
|
this.children.push(new Bolt(ctx, {
|
|
|
|
startPoint: [this.point[0], this.point[1]],
|
|
|
|
length: d * 0.8,
|
2021-07-16 16:47:18 +00:00
|
|
|
angle: this.lastAngle + randInRange(350 - this.options.spread, 370 + this.options.spread),
|
2021-04-20 00:47:40 +00:00
|
|
|
resistance: this.options.resistance,
|
|
|
|
speed: this.options.speed - 2,
|
|
|
|
spread: this.options.spread - 2,
|
2021-04-22 22:14:32 +00:00
|
|
|
branches: this.options.branches,
|
|
|
|
lineWidth: ctx.lineWidth
|
2021-04-20 00:47:40 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
this.children.forEach(child => {
|
|
|
|
child.draw(true)
|
|
|
|
})
|
|
|
|
|
|
|
|
if (isChild) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if (d < this.options.length) {
|
|
|
|
window.requestAnimationFrame(() => { this.draw() })
|
|
|
|
} else {
|
2021-04-22 22:14:32 +00:00
|
|
|
ctx.canvas.style.opacity = 1
|
2021-04-20 00:47:40 +00:00
|
|
|
this.fade()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
this.fade = function () {
|
2021-04-22 22:14:32 +00:00
|
|
|
ctx.canvas.style.opacity -= 0.04
|
|
|
|
if (ctx.canvas.style.opacity <= 0) {
|
2021-04-20 00:47:40 +00:00
|
|
|
ctx.clearRect(0, 0, window.innerWidth, window.innerHeight)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
setTimeout(() => { this.fade() }, 50)
|
|
|
|
}
|
|
|
|
}
|