You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

93 lines
3.9 KiB

function mountainHeight(position, roughness) {
let frequencies = [1721, 947, 547, 233, 73, 31, 7]
return frequencies.reduce((height, freq) => height * roughness - Math.cos(freq * position), 0)
}
export default class Godrays {
constructor(canvas) {
this.canvas = canvas
this.ctx = canvas.getContext('2d')
this.frame = 0
this.godraysCanvas = canvas.cloneNode()
this.godraysCtx = this.godraysCanvas.getContext('2d')
this.rAF = null
this.stopSign = false
this.resize = this.resize.bind(this)
this.loop = this.loop.bind(this)
this.start()
}
start() {
window.addEventListener('resize', this.resize)
this.resize()
this.loop()
}
stop() {
window.removeEventListener('resize', this.resize)
this.stopSign = true
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
this.godraysCtx.clearRect(0, 0, this.godraysCanvas.width, this.godraysCanvas.height)
}
loop() {
if (this.stopSign) {
this.rAF && window.cancelAnimationFrame(this.rAF)
return
}
this.draw()
this.rAF = window.requestAnimationFrame(this.loop)
}
draw() {
this.canvas.width = 512
this.canvas.height = 256
this.godraysCanvas.width = 128
this.godraysCanvas.height = 64
let sunY = Math.cos(this.frame++ / 512) * 24
let emissionGradient = this.godraysCtx.createRadialGradient(
this.godraysCanvas.width / 2, this.godraysCanvas.height / 2 + sunY, // The sun's center.
0, // Start radius.
this.godraysCanvas.width / 2, this.godraysCanvas.height / 2 + sunY, // Sun's center again.
44 // End radius.
)
this.godraysCtx.fillStyle = emissionGradient
emissionGradient.addColorStop(.1, '#0C0804') // Color for pixels in radius 0 to 4.4 (44 * .1).
emissionGradient.addColorStop(.2, '#060201') // Color for everything past radius 8.8.
this.godraysCtx.fillRect(0, 0, this.godraysCanvas.width, this.godraysCanvas.height)
this.godraysCtx.fillStyle = '#000'
let skyGradient = this.ctx.createLinearGradient(0, 0, 0, this.canvas.height)
skyGradient.addColorStop(0, '#2a3e55') // Blueish at the top.
skyGradient.addColorStop(.7, '#8d4835') // Reddish at the bottom.
this.ctx.fillStyle = skyGradient
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
for (let i = 0; i < 4; i++) {
this.ctx.fillStyle = `hsl(7, 23%, ${23 - i * 6}%)`
for (let x = this.canvas.width; x--;) {
let mountainPosition = (this.frame + this.frame * i * i) / 3000 + x / 2000
let mountainRoughness = i / 19 - .5
let y = 128 + i * 25 + mountainHeight(mountainPosition, mountainRoughness) * 45
this.ctx.fillRect(x, y, 1, 999)
this.godraysCtx.fillRect(x / 4, y / 4 + 1, 1, 999)
}
}
this.ctx.globalCompositeOperation = this.godraysCtx.globalCompositeOperation = 'lighter'
for (let scaleFactor = 1.07; scaleFactor < 5; scaleFactor *= scaleFactor) {
this.godraysCtx.drawImage(
this.godraysCanvas,
(this.godraysCanvas.width - this.godraysCanvas.width * scaleFactor) / 2,
(this.godraysCanvas.height - this.godraysCanvas.height * scaleFactor) / 2 - sunY * scaleFactor + sunY,
this.godraysCanvas.width * scaleFactor,
this.godraysCanvas.height * scaleFactor
)
}
this.ctx.drawImage(this.godraysCanvas, 0, 0, this.canvas.width, this.canvas.height)
}
resize() {
this.canvas.width = window.innerWidth
this.canvas.height = window.innerHeight
}
}