02 - Made in 10 minutes
/* global preloadImagesTmr fxhash fxrand palettes */
//
// fxhash - ChangeThis
//
//
// HELLO!! Code is copyright revdancatt (that's me), so no sneaky using it for your
// NFT projects.
// But please feel free to unpick it, and ask me questions. A quick note, this is written
// as an artist, which is a slightly different (and more storytelling way) of writing
// code, than if this was an engineering project. I've tried to keep it somewhat readable
// rather than doing clever shortcuts, that are cool, but harder for people to understand.
//
// You can find me at...
// https://twitter.com/revdancatt
// https://instagram.com/revdancatt
// https://youtube.com/revdancatt
//
const ratio = 1
// const startTime = new Date().getTime() // so we can figure out how long since the scene started
let drawn = false
let highRes = false // display high or low res
const features = {}
let resizeTmr = null
let aniFrame = null
window.$fxhashFeatures = {}
// Work out what all our features are
const makeFeatures = () => {
// pick a random palette
features.palette = palettes[Math.floor(fxrand() * palettes.length)]
// We need an empty array for the lines
features.lines = []
// We want between 100 and 700 lines
const numLines = fxrand() * 600 + 100
// Now make the lines witha random x, speed, size and colour
for (let i = 0; i < numLines; i++) {
features.lines.push({
x: fxrand(),
y: -0.2,
speed: fxrand() * 0.001 + 0.001,
size: fxrand() * 0.2 + 0.1,
colour: features.palette.colors[Math.floor(fxrand() * features.palette.colors.length)].value,
angle: 0,
turn: fxrand() * 2 - 1
})
}
}
// Call the above make features, so we'll have the window.$fxhashFeatures available
// for fxhash
makeFeatures()
console.log(features)
console.table(window.$fxhashFeatures)
const init = async () => {
// I should add a timer to this, but really how often to people who aren't
// the developer resize stuff all the time. Stick it in a digital frame and
// have done with it!
window.addEventListener('resize', async () => {
// If we do resize though, work out the new size...
clearTimeout(resizeTmr)
resizeTmr = setTimeout(async () => {
await layoutCanvas()
}, 100)
})
// Now layout the canvas
await layoutCanvas()
}
const layoutCanvas = async () => {
const wWidth = window.innerWidth
const wHeight = window.innerHeight
let cWidth = wWidth
let cHeight = cWidth * ratio
if (cHeight > wHeight) {
cHeight = wHeight
cWidth = wHeight / ratio
}
const canvas = document.getElementById('target')
if (highRes) {
canvas.height = 8192
canvas.width = 8192 / ratio
} else {
canvas.width = Math.min((8192 / 2), cWidth * 2)
canvas.height = Math.min((8192 / ratio / 2), cHeight * 2)
// Minimum size to be half of the high rez cersion
if (Math.min(canvas.width, canvas.height) < 8192 / 2) {
if (canvas.width < canvas.height) {
canvas.height = 8192 / 2
canvas.width = 8192 / 2 / ratio
} else {
canvas.width = 8192 / 2
canvas.height = 8192 / 2 / ratio
}
}
}
canvas.style.position = 'absolute'
canvas.style.width = `${cWidth}px`
canvas.style.height = `${cHeight}px`
canvas.style.left = `${(wWidth - cWidth) / 2}px`
canvas.style.top = `${(wHeight - cHeight) / 2}px`
// And draw it!!
drawCanvas()
}
const drawCanvas = async () => {
// Clear the animation frame
if (aniFrame) window.cancelAnimationFrame(aniFrame)
// Let the preloader know that we've hit this function at least once
drawn = true
const canvas = document.getElementById('target')
const ctx = canvas.getContext('2d')
const w = canvas.width
const h = canvas.height
// set line width
ctx.lineWidth = w / 500
// we want a finished flag so we know well all the lines agve reached the bottom
let finished = true
// Now loop through all the lines
for (let i = 0; i < features.lines.length; i++) {
const line = features.lines[i]
// set the line colour
ctx.strokeStyle = line.colour
// save the canvas state
ctx.save()
// translate the canvas to the line x and y
ctx.translate(line.x * w, line.y * h)
// rotate the canvas by the line angle
ctx.rotate(line.angle * Math.PI / 180)
// draw the line
ctx.beginPath()
ctx.moveTo(-line.size * w / 2, 0)
ctx.lineTo(line.size * w / 2, 0)
ctx.stroke()
// restore the canvas state
ctx.restore()
// move the line down
line.y += line.speed
// rotate the line
line.angle += line.turn
// if the line is not finished, set the finished flag to false
if (line.y < 1.2) finished = false
}
// If we're nit finished yet, call the draw function again
if (!finished) {
aniFrame = window.requestAnimationFrame(drawCanvas)
}
// If we have finished in two seconds reset all the y positions and start again
if (finished) {
setTimeout(() => {
for (let i = 0; i < features.lines.length; i++) {
features.lines[i].y = -0.2
}
aniFrame = window.requestAnimationFrame(drawCanvas)
}, 2000)
}
// setTimeout(() => {
//
// Call the draw function again
// aniFrame = window.requestAnimationFrame(drawCanvas)
}
const autoDownloadCanvas = async (showHash = false) => {
const element = document.createElement('a')
element.setAttribute('download', `Genuary_02_${fxhash}`)
element.style.display = 'none'
document.body.appendChild(element)
let imageBlob = null
imageBlob = await new Promise(resolve => document.getElementById('target').toBlob(resolve, 'image/png'))
element.setAttribute('href', window.URL.createObjectURL(imageBlob, {
type: 'image/png'
}))
element.click()
document.body.removeChild(element)
}
// KEY PRESSED OF DOOM
document.addEventListener('keypress', async (e) => {
e = e || window.event
// Save
if (e.key === 's') autoDownloadCanvas()
// Toggle highres mode
if (e.key === 'h') {
highRes = !highRes
console.log('Highres mode is now', highRes)
await layoutCanvas()
}
})
// This preloads the images so we can get access to them
// eslint-disable-next-line no-unused-vars
const preloadImages = () => {
if (!drawn) {
clearInterval(preloadImagesTmr)
init()
}
}
Home
Changelog
Page created in: 1ms