You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
15 KiB
TypeScript
300 lines
15 KiB
TypeScript
import { routes } from '@redwoodjs/router'
|
|
import { MetaTags } from '@redwoodjs/web'
|
|
import { useEffect, useState } from 'react'
|
|
|
|
import {
|
|
Slider, SliderTrack, SliderFilledTrack, SliderThumb, SliderMark,
|
|
Box,
|
|
Center,
|
|
Square,
|
|
Flex,
|
|
Text,
|
|
Link,
|
|
Button,
|
|
Stack, HStack, VStack, Container,
|
|
Image,
|
|
Accordion, AccordionItem, AccordionButton, AccordionPanel, AccordionIcon,
|
|
Heading
|
|
} from '@chakra-ui/react'
|
|
|
|
import Jimp from 'jimp'
|
|
import fileDownload from 'js-file-download'
|
|
import convert from 'color-convert'
|
|
|
|
const HomePage = () => {
|
|
|
|
const [h, setH] = useState(100.0)
|
|
const [s, setS] = useState(0)
|
|
const [iconB64, setIconB64] = useState("data:image/png;base64,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AAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACABhAAAAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACABhAAAAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAMIASAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAgAEEADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAgAEEADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACABhAAAAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACABhAAAAMIAAABhAAAAMIAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAMIASAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAgAEEADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAgAEEADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEAAAAwgAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAAAGEADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAIABBADAAAIAYAABADCAAAAYQAAADCAAAAYQAAADCACAAQQAwAACAGAAAQAwgAAAGEAAAAwgAMA9C1TgCKSLscS8AAAAAElFTkSuQmCC")
|
|
let base, bottom, middle, top
|
|
|
|
// This is the onload effect
|
|
useEffect(() => {
|
|
const loadImages = async () => {
|
|
base = await Jimp.read('/img/base.png')
|
|
bottom = await Jimp.read('/img/bottom.png')
|
|
middle = await Jimp.read('/img/middle.png')
|
|
top = await Jimp.read('/img/top.png')
|
|
updateIcon(h, s).then( (val) => {
|
|
setIconB64(val)
|
|
})
|
|
}
|
|
loadImages()
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
updateIcon(h, s).then( (string) => {
|
|
console.log("Changing iconB64")
|
|
setIconB64(string)
|
|
})
|
|
}, [h, s])
|
|
|
|
const updateIcon = async (hue, sat) => {
|
|
const base = await Jimp.read('/img/base.png')
|
|
const feature = await Jimp.read('/img/feature.png')
|
|
const constructed = await base
|
|
.clone()
|
|
.composite(
|
|
feature
|
|
.color([
|
|
{apply: 'hue', params: [hue]},
|
|
{apply: 'saturate', params: [sat]}
|
|
]),
|
|
0,
|
|
0,
|
|
{ mode: Jimp.BLEND_SOURCE_OVER }
|
|
)
|
|
const string = await constructed.getBase64Async(Jimp.MIME_PNG)
|
|
return string
|
|
}
|
|
|
|
|
|
const saveIcon = async () => {
|
|
const jimpImage = await Jimp.read(
|
|
Buffer.from(iconB64.split(',')[1], 'base64')
|
|
)
|
|
jimpImage.getBuffer(Jimp.MIME_PNG, (err, buffer) => {
|
|
fileDownload(buffer, 'snowglob.png')
|
|
})
|
|
}
|
|
|
|
function gradient(hue) {
|
|
const offset = 210
|
|
const start = convert.hsv.hex((hue + offset) % 360, 27.3, 64.7)
|
|
const end = convert.hsv.hex((hue + offset) % 360, 100, 100)
|
|
const string = 'linear(to-r, #' + start + ', #' + end + ')'
|
|
console.log(string)
|
|
return string
|
|
}
|
|
|
|
return (
|
|
<>
|
|
<MetaTags
|
|
title="Snowglobs for everyone!"
|
|
description="The snowglobs website for all your snowglob needs."
|
|
ogUrl="https://www.snowglobs.com"
|
|
ogContentUrl="https://www.snowglobs.com/snowglobs-logo.jpg"
|
|
ogImage="https://www.snowglobs.com/snowglobs-logo.jpg"
|
|
robots={['nofollow']}
|
|
/>
|
|
<Center>
|
|
<Box p={4} maxW='580px' overflow='hidden'>
|
|
<div>
|
|
<Center>
|
|
<Image
|
|
src="/img/snowglobs.png"
|
|
alt="Snowglobs Logo"
|
|
className="dark:invert"
|
|
width={417}
|
|
height={85}
|
|
/>
|
|
</Center>
|
|
<Text align="center">
|
|
Create and download a snowglob in the hue of your choice.
|
|
</Text>
|
|
<Text align="center">
|
|
Apologies for the 4s it takes to render a snowglob. Excellence takes time.
|
|
</Text>
|
|
</div>
|
|
|
|
<Box>
|
|
<VStack
|
|
spacing={4}
|
|
align='stretch'
|
|
>
|
|
<Flex id="hues" h='40px' style={{
|
|
backgroundImage: 'url(/img/colorbar.png)',
|
|
backgroundSize: "contain"
|
|
}}>
|
|
<Slider
|
|
aria-label='slider-ex-1'
|
|
defaultValue={ h }
|
|
min={0} max={360} step={0.1}
|
|
onChangeEnd={(val) => setH(val)}
|
|
>
|
|
<SliderTrack>
|
|
</SliderTrack>
|
|
<SliderThumb />
|
|
</Slider>
|
|
</Flex>
|
|
<Flex id="hues" h='40px' bgGradient={ gradient(h) }>
|
|
<Slider
|
|
aria-label='slider-ex-1'
|
|
defaultValue={ s }
|
|
min={0} max={100} step={1}
|
|
onChangeEnd={(val) => setS(val)}
|
|
>
|
|
<SliderTrack>
|
|
</SliderTrack>
|
|
<SliderThumb />
|
|
</Slider>
|
|
</Flex>
|
|
<Flex>
|
|
<Square size='104px' bg='teal'>
|
|
<Image
|
|
src={ iconB64 }
|
|
alt="Snowglobs Logo"
|
|
className="dark:invert"
|
|
width={100}
|
|
height={100}
|
|
/>
|
|
</Square>
|
|
<Center flex='1'>
|
|
<Button
|
|
variant='solid'
|
|
colorScheme="blue"
|
|
size="lg"
|
|
onClick={(e) => saveIcon()}
|
|
>
|
|
Download
|
|
</Button>
|
|
</Center>
|
|
</Flex>
|
|
</VStack>
|
|
</Box>
|
|
<Box p={8}>
|
|
<Center p={4}><Heading size="md" as="h1">FAQ</Heading></Center>
|
|
<Accordion>
|
|
<AccordionItem>
|
|
<h2>
|
|
<AccordionButton>
|
|
<Box as="span" flex='1' textAlign='left'>
|
|
<Heading size="sm" as="h3">
|
|
What is a snowglob?
|
|
</Heading>
|
|
</Box>
|
|
<AccordionIcon />
|
|
</AccordionButton>
|
|
</h2>
|
|
<AccordionPanel pb={4}>
|
|
A snowglob is an abstract representation of a snow globe. A snow globe is a transparent or
|
|
opaque sphere, traditionally made of glass, usually enclosing a miniaturized scene of some
|
|
sort, often together with a model of a town, neighborhood, landscape or figure.
|
|
<br></br><br></br>
|
|
Snowglobs do not cointains a miniaturized scene.
|
|
</AccordionPanel>
|
|
</AccordionItem>
|
|
<AccordionItem>
|
|
<h2>
|
|
<AccordionButton>
|
|
<Box as="span" flex='1' textAlign='left'>
|
|
<Heading size="sm" as="h3">Why are they called snowglobs?</Heading>
|
|
</Box>
|
|
<AccordionIcon />
|
|
</AccordionButton>
|
|
</h2>
|
|
<AccordionPanel pb={4}>
|
|
Because the domain snowglobes.com was taken, and my domain registrar suggested snowglobs
|
|
as an alternative
|
|
</AccordionPanel>
|
|
</AccordionItem>
|
|
<AccordionItem>
|
|
<h2>
|
|
<AccordionButton>
|
|
<Box as="span" flex='1' textAlign='left'>
|
|
<Heading size="sm" as="h3">
|
|
They look an awful lot like the LinkedIn default avatar
|
|
</Heading>
|
|
</Box>
|
|
<AccordionIcon />
|
|
</AccordionButton>
|
|
</h2>
|
|
<AccordionPanel pb={4}>
|
|
That is not a question. However, any resemblence is a complete coincidence.
|
|
LinkedIn default avatars are a circle overlapping an eye shape in a circle.
|
|
I designed snowglobs as a circle overlapping an oval, and the image is on a square.
|
|
<br></br><br></br>
|
|
Furthermore,
|
|
snowglobs are different colors to the LinkedIn no-profile picture avatars.
|
|
<br></br><br></br>
|
|
They are meant to be snowglobs.
|
|
</AccordionPanel>
|
|
</AccordionItem>
|
|
<AccordionItem>
|
|
<h2>
|
|
<AccordionButton>
|
|
<Box as="span" flex='1' textAlign='left'>
|
|
<Heading size="sm" as="h3">Am I free to use my snowglob as I see fit?</Heading>
|
|
</Box>
|
|
<AccordionIcon />
|
|
</AccordionButton>
|
|
</h2>
|
|
<AccordionPanel pb={4}>
|
|
You absolutely are. I hereby release all snowglob images into the public domain under
|
|
the
|
|
<Link color="teal.500" href="https://creativecommons.org/publicdomain/zero/1.0/?ref=chooser-v1">
|
|
CC0 1.0 Universal license
|
|
</Link>.
|
|
</AccordionPanel>
|
|
</AccordionItem>
|
|
<AccordionItem>
|
|
<h2>
|
|
<AccordionButton>
|
|
<Box as="span" flex='1' textAlign='left'>
|
|
<Heading size="sm" as="h3">Are you going to release NFTs of snowglobs?</Heading>
|
|
</Box>
|
|
<AccordionIcon />
|
|
</AccordionButton>
|
|
</h2>
|
|
<AccordionPanel pb={4}>
|
|
It is on my to-do list. There will be 1800 snowglobs, each with a subtly different shade.
|
|
<br></br><br></br>
|
|
I just haven't gotten around to minting them yet.
|
|
</AccordionPanel>
|
|
</AccordionItem>
|
|
<AccordionItem>
|
|
<h2>
|
|
<AccordionButton>
|
|
<Box as="span" flex='1' textAlign='left'>
|
|
<Heading size="sm" as="h3">Why did you build snowglobs?</Heading>
|
|
</Box>
|
|
<AccordionIcon />
|
|
</AccordionButton>
|
|
</h2>
|
|
<AccordionPanel pb={4}>
|
|
I find them aesthetically pleasing.
|
|
<br></br><br></br>
|
|
What I certainly did not do, is design them as replacement profile pictures for LinkedIn.
|
|
Please do not replace your LinkedIn profile picture with a snowglob. If you use a snowglob
|
|
as a profile picture, under the terms and conditions of the site you may find that LinkedIn
|
|
replaces your snowglob picture with the the LinkedIn default avatar.
|
|
<br></br><br></br>
|
|
And that would be terrible!
|
|
</AccordionPanel>
|
|
</AccordionItem>
|
|
</Accordion>
|
|
</Box>
|
|
<Box>
|
|
<Center>
|
|
A project conceived and badly implemented by
|
|
<Link color="teal.500" href="https://www.linkedin.com/in/keirf/">BlockchainGandalf</Link>
|
|
</Center>
|
|
</Box>
|
|
</Box>
|
|
</Center>
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default HomePage
|