Przebyliśmy długą drogę od podstaw przeglądarki. Narzędzia takie jak „Three.js” i „WebGL Studio”, biblioteki animacji, takie jak „Raphael.js” lub „GSAP”, rosnąca popularność „SVG” i coraz bardziej sprzyjające zmiany w „Javascript” w ostatniej dekadzie zmieniły animacje w przeglądarce od sporadycznej nowości do czegoś prawdziwego i przyjemnego do zabawy. Kanwa HTML to świetny sposób na zmoczenie nóg podczas tworzenia bezpośrednio interaktywnych wizualizacji w przeglądarce. Ale animacji jest dużo i najlepiej zacząć od czegoś małego. W tym samouczku nauczymy się, jak stworzyć prostą chmurę cząstek przy użyciu pełnoekranowego płótna, animować ją i sprawić, by reagowała na działania użytkownika.
Pod koniec tego samouczka utworzysz element składający się z
małych kolorowych cząstek, które obracają się wokół pozycji myszy; gdy lewy przycisk myszy jest wciśnięty, efekt cząsteczkowy będzie skalowany tak, aby zakreślił cały ekran. Mamy nadzieję, że ten artykuł da każdemu nowicjuszowi Canvas kilka pomysłów na zabawę.
Wypróbuj demo tutaj: http://particle-vortex.herokuapp.com/
Przeczytaj pełny kod źródłowy tutaj: https://github.com/IsaacBell/Canvas-Particle-Vortex
Zmienne początkowe
Najpierw ustalmy warunki początkowe i zmienne efektu. Dzięki temu w jednym miejscu możemy dokonać globalnych zmian. Tutaj ustawimy niezbędne zmienne dla elementu canvas.
# Initial Setup
fps = 30 width = window.innerWidth height = window.innerHeight r = 70 scale = 1 scaleMin = 12.5 scaleMax = 100 particleCount = 250 canvas = undefined # You can of course omit this line context = undefined # Ditto particles = undefined # Ditto mouseX = width * 0.5 mouseY = height * 0.5 isMouseDown = false
Ustawiamy zmienną przechowującą rozmiar okna wyświetlania, przekazujemy tę informację do elementu canvas w celu zdefiniowania jego granic. Zmienna r
określi promień chmury cząstek. skala zmienia rozmiar chmury, scaleMin
pozwala nam ustawić bezwzględnie minimalną skalę chmury, a scaleMax
pozwala ustawić maksymalną skalę, jaką osiągnie chmura, gdy użytkownik będzie przytrzymywał mysz. particleCount
pozwala zwiększyć lub zmniejszyć liczbę cząstek pojawiających się w elemencie; za dużo i wystąpią problemy z wydajnością. Na potrzeby tego dema 30 FPS będzie wystarczające.
Nasza funkcja inicjująca
init = ->
# Create canvas element
canvas = document.createElement('canvas')
canvas.id = 'myCanvas'
document.body.appendChild(canvas)
if canvas and canvas.getContext
context = canvas.getContext('2d')
# Event handlers
window.addEventListener 'mousemove', onMouseMove, false
window.addEventListener 'mousedown', onMouseDown, false
window.addEventListener 'mouseup', onMouseUp, false
window.addEventListener 'onResize', onResize, false
createParticles()
onResize()
setInterval animLoop, 1000 / fps
return
Oto, co dzieje się w kodzie:
Krok 1
Tworzymy element canvas i ustawiamy odpowiadający mu identyfikator elementu HTML na „myCanvas”. Będziesz potrzebować elementu o tym identyfikatorze w kodzie HTML, aby dopasować.
Krok 2
Dołączamy (dodajemy) element canvas do treści naszego kodu HTML. Jeśli pominiemy ten krok, płótno nigdy się nie pojawi, ponieważ nie jest nigdzie rysowane/renderowane w dokumencie HTML
Krok 3
Jeśli płótno zostało poprawnie ustawione i możemy dokonać konfiguracji za pomocą metody getContext()
-› Ustawiamy kontekst rysowania na „2D”
-› Dodajemy detektory zdarzeń dla zdarzeń myszy i zmiany rozmiaru przeglądarki
-› Wywołujemy funkcję createParticles(), której użyjemy do wygenerowania naszej chmury cząstek
-› Wywołujemy funkcję onResize(), która dopasuje nasz element canvas do potrzebnych proporcji
-› Ustawiamy animLoop() tak, aby powtarzała się z szybkością naszej zmiennej fps
Kolor
Pierwszą funkcją, którą zdefiniujemy, jest colorLuminance(). Potrzebujemy tego, gdy generujemy nasze cząstki; użyjemy tego, gdy rozjaśnimy lub przyciemnimy kolory szesnastkowe, które wygenerujemy podczas tworzenia naszej chmury cząstek. funkcja colorLuminance() przyjmuje dwa argumenty, pierwszy to ciąg szesnastkowy reprezentujący kolor, a drugi to liczba dziesiętna z zakresu od 1 do -1, wskazująca, jak bardzo rozjaśnić lub przyciemnić kolor szesnastkowy.
Więcej szczegółów na temat tego, co się tutaj dzieje, można znaleźć w tym „artykule Craiga Bucklera”.
colorLuminance = (hex, lum) -> # validate hex string hex = String(hex).replace(/[^0-9a-f]/gi, '') if hex.length < 6 hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] lum = lum or 0
# convert to decimal and change luminosity rgb = '#' c = undefined i = undefined i = 0 while i < 3 c = parseInt(hex.substr(i * 2, 2), 16) c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16) rgb += ('00' + c).substr(c.length) i++ return rgb
Mając to na uwadze, stwórzmy funkcję, która będzie służyć jako główna część naszego projektu.
Tworzenie naszych cząstek
Użyjemy pętli while, aby utworzyć szereg cząstek o losowych prędkościach ruchu, kolorze wypełnienia i odległości orbity od środka chmury. Wartość fillColor
korzysta z funkcji colorLuminance()
, którą właśnie dodaliśmy wcześniej; pobaw się tą linią, aby znaleźć zakres kolorów, który Ci się podoba. Prędkość i odległość orbity każdej cząstki są losowane w pewnym stopniu poprzez pomnożenie wartości przez wartość zwróconą przez Math.random()
. Jest to bardzo powszechny wzór w przetwarzaniu grafiki/kolorów.
createParticles = ->
particles = []
i = 0
while i < particleCount
particle =
size: 5
position:
x: mouseX
y: mouseY
offset:
x: 0
y: 0
shift:
x: mouseX
y: mouseY
speed: 0.02 + Math.random() * 0.02
targetSize: 1
fillColor: colorLuminance('#' + Math.random().toString(16), -0.23)
orbit: r / 3 * Math.random()
particles.push particle
i++
return
Odpowiadanie na słuchaczy zdarzeń
Następnie napiszemy funkcje, które będą uruchamiane przez skonfigurowane wcześniej detektory zdarzeń. Nic zbyt skomplikowanego, po prostu przechowujemy dynamiczną pozycję myszy i status myszy w naszych zmiennych najwyższego poziomu. Na koniec, gdy zmienimy rozmiar okna przeglądarki, zmienimy wymiary elementu canvas.
# Simple event Listener functions
onMouseMove = (e) -> mouseX = e.clientX - ((window.innerWidth - width) * .5) mouseY = e.clientY - ((window.innerHeight - height) * .5) return
onMouseDown = -> isMouseDown = true
onMouseUp = -> isMouseDown = false
onResize = -> width = window.innerWidth height = window.innerHeight canvas.width = width canvas.height = height return
Pętla animacji
Teraz o samej pętli renderowania. To tutaj rozgrywa się prawdziwa akcja; nie daj się zastraszyć, patrząc na to.
Oto podsumowanie tego, co robimy:
- Skalujemy chmurę cząstek do jej maksymalnego lub minimalnego rozmiaru w zależności od tego, czy mysz jest opuszczona
- Za pomocą kontekstu.fillStyle() ustawiamy kolor rysowania kształtów. Wybieramy czarny z kryciem 55%
- Za pomocą kontekstu.fillRect() wyznaczamy granice prostokąta płótna, który będziemy wypełniać
- Używając prostej pętli while, iterujemy po cząstkach oraz obracamy, przesuwamy i delikatnie powiększamy każdą cząstkę, jeśli to konieczne. Im większa jest zmienna fps, tym szybsze będą te zmiany i obroty
- Po obliczeniu nowego rozmiaru i położenia naszej cząstki w każdej iteracji używamy interfejsu API canvas do narysowania naszego okręgu
animLoop = -> if isMouseDown # Expand the cloud when mouse is clicked down scale += (scaleMax - scale) * 0.2 else # Or else shrink the cloud down scale -= (scale - scaleMin) * 0.2 # Apply whichever change we set above scale = Math.min(scale, scaleMax)
# Set our line opacity and limit our drawing board # size to the size of the screen context.fillStyle = 'rgba(0,0,0,0.55)' context.fillRect 0, 0, context.canvas.width, context.canvas.height i = 0 len = particles.length while i < len particle = particles[i] # Rotation particle.offset.x += particle.speed * 0.7 particle.offset.y += particle.speed * 0.7 # Follow the mouse, with a bit of blur/lag effect particle.shift.x += (mouseX - (particle.shift.x)) * particle.speed * 0.6 particle.shift.y += (mouseY - (particle.shift.y)) * particle.speed * 0.6 # Shift the particles accordingly, using a cosine function particle.position.x = particle.shift.x + Math.cos(i + particle.offset.x) * particle.orbit * scale particle.position.y = particle.shift.y + Math.sin(i + particle.offset.y) * particle.orbit * scale # Limit our animation to the screen bounds particle.position.x = Math.max(Math.min(particle.position.x, width), 0) particle.position.y = Math.max(Math.min(particle.position.y, height), 0) particle.size += (particle.targetSize - (particle.size)) * 0.05 if Math.round(particle.size) == Math.round(particle.targetSize) particle.targetSize = 1 + Math.random() * 10 # Finally, let's do some drawing! context.beginPath() # Select line color at random context.fillStyle = particle.fillColor context.strokeStyle = particle.fillColor context.lineWidth = particle.size context.moveTo particle.position.x, particle.position.y context.lineTo particle.position.x, particle.position.y context.stroke() context.arc particle.position.x, particle.position.y, particle.size / 2, 0, Math.PI * 2, true context.fill() i++ return
Na koniec musimy wywołać naszą funkcję inicjującą, aby przeskoczyć pewne rzeczy.
window.onload = init
Wniosek
Mamy nadzieję, że wiesz, jak przenieść w pełni interaktywne wizualizacje do przeglądarki internetowej za pomocą obszaru roboczego. To demo można łatwo rozszerzyć, zoptymalizować lub zmodyfikować. Jest mnóstwo potencjału. Pobaw się zmiennymi, pobierz źródło na Githubie, poeksperymentuj w przeglądarce.
Repozytorium Github: https://github.com/IsaacBell/Canvas-Particle-Vortex