REVDANCATT'S GENUARY 2023

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()
  }
}

Project files


Home
Changelog
Page created in: 1ms