Skip to content

Add particles #88

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions assets/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "./css/app.css"

import algoliasearch from 'algoliasearch/dist/algoliasearch.esm.browser'
import { autocomplete, getAlgoliaResults } from "@algolia/autocomplete-js"
import Particles from "./js/particles"
import { Application } from "stimulus"
import { TransitionController, ClickOutsideController } from 'stimulus-use'
import MenuController from './js/controllers/menu_controller'
Expand All @@ -17,6 +18,13 @@ application.register("select", SelectController)
application.register("mode-switch", ModeSwitchController)
application.register("flyover", FlyoverController)

const particlesSection = document.getElementById("title-section")

if (particlesSection) {
const particles = new Particles(particlesSection)
particles.start()
}

// Search
const searchClient = algoliasearch(
'SIHVOPCWNI',
Expand Down
10 changes: 7 additions & 3 deletions assets/src/css/_base.css
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ body {
}

strong {
@apply font-medium;
@apply font-semibold;
}

html.dark body {
@apply bg-dark-secondary text-dark-primary;
}

html.dark strong {
@apply font-semibold;
#title-section canvas {
@apply bg-light-primary
}

html.dark #title-section canvas {
@apply bg-gray-800
}

/* Vercel */
Expand Down
218 changes: 218 additions & 0 deletions assets/src/js/particles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
class Particle {
constructor(canvas, container) {
const random = Math.random()
const containerWidth = container.offsetWidth
const containerHeight = container.offsetHeight

this.progress = 0;
this.canvas = canvas
this.center = {
x: containerWidth / 2,
y: containerHeight / 2
}
this.pointOfAttraction = {
x: containerWidth / 2,
y: containerHeight / 2
}

if (Math.random() > 0.5) {
this.x = containerWidth * Math.random()
this.y = Math.random() > 0.5 ? -Math.random() - 100 : containerHeight + Math.random() + 100
} else {
this.x = Math.random() > 0.5 ? -Math.random() - 100 : containerWidth + Math.random() + 100
this.y = containerHeight * Math.random()
}

this.s = Math.random() * 2
this.a = 0
this.w = containerWidth
this.h = containerHeight
this.radius = random > .2 ? Math.random() : Math.random() * 3
this.color = random > .2 ? "#FB7185" : "#38BDF8"
this.radius = random > .8 ? Math.random() * 2.2 : this.radius
this.color = random > .8 ? "#818CF8" : this.color
}

calculateDistance(v1, v2){
let x = Math.abs(v1.x - v2.x)
let y = Math.abs(v1.y - v2.y)

return Math.sqrt((x * x) + (y * y))
}

render(){
this.canvas.beginPath()
this.canvas.arc(this.x, this.y, this.radius, 0, 2 * Math.PI)
this.canvas.lineWidth = 2
this.canvas.fillStyle = this.color
this.canvas.fill()
this.canvas.closePath()
}

move(){
const p1 = {
x: this.x,
y: this.y
}

const distance = this.calculateDistance(p1, this.pointOfAttraction)
const force = Math.max(100, (1 + distance))

const attr_x = (this.pointOfAttraction.x - this.x) / force
const attr_y = (this.pointOfAttraction.y - this.y) / force

this.x += (Math.cos(this.a) * (this.s)) + attr_x
this.y += (Math.sin(this.a) * (this.s)) + attr_y
this.a += (Math.random() > 0.5 ? Math.random() * 0.9 - 0.45 : Math.random() * 0.4 - 0.2)

if (distance < (10 + Math.random()*100)){
return false
}

this.render()
this.progress++

return true
}
}

class CenterParticle{
constructor(canvas, container){
let random = Math.random()
const containerWidth = container.offsetWidth
const containerHeight = container.offsetHeight

this.progress = 0
this.canvas = canvas

this.x = containerWidth / 2 + (Math.random()*200 - Math.random() * 200)
this.y = containerHeight / 2 + (Math.random()*200 - Math.random() * 200)

this.w = containerWidth
this.h = containerHeight
this.radius = random > .2 ? Math.random() : Math.random() *3
this.color = random > .2 ? "#E879F9" : "#818CF8"
this.radius = random > .8 ? Math.random()*2 : this.radius
this.color = random > .8 ? "#38BDF8" : this.color

// this.color = random > .1 ? "#ffae00" : "#f0ff00" // Alien
this.variantx1 = Math.random()*300
this.variantx2 = Math.random()*400
this.varianty1 = Math.random()*100
this.varianty2 = Math.random()*120
}

render() {
this.canvas.beginPath();
this.canvas.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
this.canvas.lineWidth = 2;
this.canvas.fillStyle = this.color;
this.canvas.fill();
this.canvas.closePath();
}

move(){
// this.x += (Math.sin(this.progress/this.variantx1)*Math.cos(this.progress/this.variantx2));
// this.y += (Math.sin(this.progress/this.varianty1)*Math.cos(this.progress/this.varianty2));
this.x += (Math.sin(this.progress/this.variantx1) * Math.cos(this.progress/this.variantx2))
this.y += (Math.cos(this.progress/this.varianty2))

if (this.x < 0 || this.x > this.w - this.radius) {
return false
}

if (this.y < 0 || this.y > this.h - this.radius) {
return false
}

this.render()
this.progress++

return true
}
}

export default class Particles {
constructor(container, maxParticles = 100, frequency = 20) {
this.container = container
this.maxParticles = maxParticles
this.frequency = frequency
this.particles = []
this.maxTime = this.frequency * this.maxParticles
this.timeToRecreate = false
this.fillColor = {
"light": "#F0F4FC",
"dark": "#0F172A"
}
}

start() {
setTimeout(function () {
this.timeToRecreate = true
}.bind(this))

this.canvasElement = document.createElement("canvas")
this.canvasElement.width = this.container.offsetWidth
this.canvasElement.height = this.container.offsetHeight
this.container.prepend(this.canvasElement)

this.canvas = this.canvasElement.getContext("2d")

const observer = new MutationObserver(() => {
this.clear(true)
})

observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class'],
childList: false,
characterData: false
})

window.addEventListener("resize", () => {
this.clear(false, true)
})

this._doStart()
}

_doStart() {
this.populate(this.maxParticles)

this.update()
}

populate(numberOfParticles) {
for (let i = 0; i < numberOfParticles; i++) {
setTimeout(function() {
this.particles.push(new CenterParticle(this.canvas, this.container))
}.bind(this), this.frequency * i)
}

return this.particles.length
}

clear(redraw = false, resize = false) {
if (resize) {
this.canvasElement.width = this.container.offsetWidth
this.canvasElement.height = this.container.offsetHeight
}

this.canvas.globalAlpha = 0.30
this.canvas.globalCompositeOperation = "destination-out"
this.canvas.fillRect(0, 0, this.canvasElement.width, this.canvasElement.height)
this.canvas.globalCompositeOperation = "source-over"
this.canvas.globalAlpha = 1
}

update() {
this.particles = this.particles.filter((p) => { return p.move() })

if (this.timeToRecreate && this.particles.length < this.maxParticles) {
this.populate(1)
}

this.clear()
requestAnimationFrame(this.update.bind(this))
}
}
49 changes: 26 additions & 23 deletions themes/poetry/layouts/home.html
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
{{ define "main" }}
<div class="flex flex-wrap p-16 mx-auto max-w-7xl h-screen relative px-4 sm:p-24">
<div class="flex-grow self-center text-center">
<p
class="mt-3 mb-3 max-w-md mx-auto text-sm font-medium tracking-wide uppercase opacity-70 md:mt-5 md:max-w-3xl">
Python packaging and dependency management made easy
</p>
<h1 class="text-5xl tracking-tight font-light sm:text-5xl md:text-5xl">
<span class="text-blue-500 dark:text-blue-400">P</span>oetry
</h1>
</div>
<div class="absolute flex-grow flex items-center justify-between bottom-0 right-0 w-full">
<div class="mx-auto self-start animate-bounce">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512" class="w-10 h-10">
<g>
<path fill="#38BDF8" fill-opacity="40%"
d="M160 256.14l-56.51 56.47-96.44-96.15a23.77 23.77 0 0 1-.18-33.61l.18-.18 22.59-22.51a23.94 23.94 0 0 1 33.85 0z">
</path>
<path fill="currentColor"
d="M313 182.57L290.21 160a23.94 23.94 0 0 0-33.85 0L103.47 312.61 143 352l.06.06a24 24 0 0 0 33.93-.16L313 216.36l.18-.17a23.78 23.78 0 0 0-.18-33.62z">
</path>
</g>
</svg>
<div class="relative">
<div id="title-section" class="absolute top-0 left-0 h-screen w-full bg-light-primary dark:bg-gray-800"></div>
<div class="flex flex-wrap p-16 mx-auto max-w-7xl h-screen relative px-4 sm:p-24">
<div class="flex-grow self-center text-center">
<p
class="mt-3 mb-3 max-w-md mx-auto text-sm font-medium tracking-wide uppercase opacity-70 md:mt-5 md:max-w-3xl">
Python packaging and dependency management made easy
</p>
<h1 class="text-5xl tracking-tight font-light sm:text-5xl md:text-5xl">
<span class="text-blue-500 dark:text-blue-400">P</span>oetry
</h1>
</div>
<div class="absolute flex-grow flex items-center justify-between bottom-0 right-0 w-full">
<div class="mx-auto self-start animate-bounce">
<svg aria-hidden="true" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 320 512" class="w-10 h-10">
<g>
<path fill="#38BDF8" fill-opacity="40%"
d="M160 256.14l-56.51 56.47-96.44-96.15a23.77 23.77 0 0 1-.18-33.61l.18-.18 22.59-22.51a23.94 23.94 0 0 1 33.85 0z">
</path>
<path fill="currentColor"
d="M313 182.57L290.21 160a23.94 23.94 0 0 0-33.85 0L103.47 312.61 143 352l.06.06a24 24 0 0 0 33.93-.16L313 216.36l.18-.17a23.78 23.78 0 0 0-.18-33.62z">
</path>
</g>
</svg>
</div>
</div>
</div>
</div>
Expand Down