Efekty typograficzne na płótnie

Moje tło

<canvas> dotarło do mnie w 2006 roku, kiedy pojawiła się przeglądarka Firefox v2.0. Artykuł na temat Ajaxian, opisujący macierz transformacji, zainspirował mnie do utworzenia mojej pierwszej aplikacji internetowej <canvas>Color Sphere (2007). To one pozwoliły mi zagłębić się w świat kolorów i elementy graficzne oraz zainspirować go do stworzenia Sketchpada (2007–2008) w celu utworzenia aplikacji „lepszej niż Paint” w przeglądarce. Eksperymenty te doprowadziły do powstania startupu Mugtug z wieloletnim przyjacielem Charlesem Pritchardem. Pracujemy nad ciemnią w HTML5 <canvas>. Darkroom to nieszkodliwa aplikacja do udostępniania zdjęć, która łączy możliwości filtrów pikselowych z typografii i rysowaniem opartym na wektorach.

Wstęp

Grafika banera Canvas.

<canvas> zapewnia programistom JavaScriptu pełną kontrolę nad kolorami, wektorami i pikselami na ekranach – wizualną konstrukcją monitora.

Poniższe przykłady dotyczą jednego obszaru w regionie <canvas>, który nie przyciągał dużej uwagi,: tworzenia efektów tekstowych. Różnorodność efektów tekstowych, które można utworzyć w usłudze <canvas>, jest ogromna, a prezentacje obejmują podsekcję możliwości. Chociaż w tym artykule mówimy o „tekście”, metody te można zastosować do dowolnych obiektów wektorowych, np. do tworzenia ekscytujących treści wizualnych w grach i w innych zastosowaniach:

Cienie tekstu w aplikacji <canvas>.
Efekty tekstowe podobne do CSS w <canvas> pozwalają tworzyć maski przycinania, znajdować dane w <canvas> i korzystać z właściwości cienia.
Neonowa tęcza, odbicie zebry – łańcuszki.
Efekty tekstowe podobne do Photoshop w <canvas> przykładach użycia: globalCompositeOperation, createLinearGradient, createPattern.
Cienie wewnętrzne i zewnętrzne w: <canvas>
Odkrycie mało znanej funkcji; przewijanie w prawo i w lewo, by utworzyć odwrotność cienia (wewnętrzny cień).
Spaceage – efekt generatywny.
Oparty na generatywnej AI efekt tekstowy w technologii <canvas> wykorzystujący cykle kolorów hsl() i window.requestAnimationFrame, aby stworzyć wrażenie ruchu.

Cienie tekstu w Canvas

Jednym z moich ulubionych dodatków do specyfikacji CSS3 (wraz z obramowaniem, gradientem sieciowym i innymi) jest możliwość tworzenia cieni. Musisz zdać sobie sprawę z różnic między cieniami CSS i <canvas>, a w szczególności:

CSS korzysta z dwóch metod: box-shadow dla elementów, takich jak div, span itd. oraz text-shadow dla zawartości tekstowej.

<canvas> ma jeden typ cienia: wszystkie obiekty wektorów: ctx.moveTo, ctx.lineTo, ctx.bezierCurveTo, ctx.quadradicCurveTo, ctx.arc, ctx.rect, ctx.fillText, ctx.slideText i tak dalej. Aby utworzyć cień w elemencie <canvas>, skorzystaj z tych 4 właściwości:

ctx.shadowColor = "red" // ciąg znaków
Kolor cienia: prawidłowe są dane wejściowe RGB, RGBA, HSL, HEX i inne.
ctx.shadowOffsetX = 0; // liczba całkowita
Odległość cienia w poziomie względem tekstu.
ctx.shadowOffsetY = 0; // liczba całkowita
Odległość cienia w pionie względem tekstu.
ctx.shadowBlur = 10; // liczba całkowita
Efekt rozmycia cienia, im większa wartość, tym większe rozmycie.

Na początek zobaczmy, jak <canvas> może emulować efekty CSS. Po wyszukaniu w Grafice Google frazy „css text-shadow” znaleźliśmy kilka świetnych demonstracji do naśladowania: Line25, Stereoskopowy i Shadow 3D.

Grafika 3D CSS

Stereoskopowy efekt 3D (więcej informacji znajdziesz w sekcji Obraz przedstawiający anaglif) to przykład prostej linii kodu, który można wykorzystać w praktyce. Poniższa linia CSS pozwala stworzyć iluzję głębi podczas oglądania w 3D, czerwonych/błękitnych okularach (rodzaju efektu w filmach 3D):

text-shadow: -0.06em 0 0 red, 0.06em 0 0 cyan;

Podczas konwertowania tego ciągu znaków na ciąg <canvas> należy zwrócić uwagę na 2 rzeczy:

  1. Nie ma tu efektu rozmycia cienia (trzeciej wartości), więc nie ma powodu, by stosować cień, ponieważ FillText zapewniłby te same wyniki:
var text = "Hello world!"
ctx.fillStyle = "#000"
ctx.fillText(text, -7, 0);
ctx.fillStyle = "red"
ctx.fillText(text, 0, 0);
ctx.fillStyle = "cyan"
ctx.fillText(text, 7, 0);</pre>
  1. Jednostki EM nie są obsługiwane w języku <canvas>, więc trzeba je przekonwertować na pliki PX. Możemy określić współczynnik konwersji dla konwersji między PT, PC, EM, EX, PX itd., tworząc element o tych samych właściwościach czcionki w DOM i ustawiając szerokość na zmierzony format. Aby zarejestrować konwersję EM -> PX, zmierzyliśmy element DOM z wartością „wysokość: 1em”.
var font = "20px sans-serif"
var d = document.createElement("span");
d.style.cssText = "font: " + font + " height: 1em; display: block"
// the value to multiply PX 's by to convert to EM 's
var EM2PX = 1 / d.offsetHeight;</pre>

Zapobieganie mnożeniu alfa

W bardziej złożonym przykładzie, np. w przypadku efektu neonu w wierszu 25, do prawidłowej emulacji efektu należy użyć właściwości shadowBlur. Efekt neonów bazuje na wielu cieniach, więc mamy problem, ponieważ w obiekcie <canvas> każdy obiekt wektorowy może mieć tylko jeden cień. Aby narysować wiele cieni, musisz narysować wiele wersji tekstu. Powoduje to mnożenie alfa i w efekcie postrzępione krawędzie.

Neonowa grafika

Uruchomiłem ctx.fillStyle = "rgba(0,0,0,0)" lub "transparent", aby ukryć tekst, wyświetlając cień... jednak ta próba nie powiodła się, ponieważ cień jest mnożeniem wartości alfa FillStyle, więc cień nigdy nie może być bardziej nieprzezroczysty niż FillStyle.

Na szczęście jest na to sposób. Możemy narysować przesunięcie cienia od tekstu, rozdzielając te fragmenty (aby nie nakładały się na siebie), i ukryć tekst z boku ekranu:

var text = "Hello world!"
var blur = 10;
var width = ctx.measureText(text).width + blur * 2;
ctx.textBaseline = "top"
ctx.shadowColor = "#000"
ctx.shadowOffsetX = width;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -width, 0);

Zacinanie się wokół bloku tekstu

Aby trochę to oczyścić, dodaj ścieżkę przycinania, uniemożliwiając narysowanie tekstu fillText w pierwszej kolejności (jednocześnie umożliwiając rysowanie cienia). Aby utworzyć ścieżkę przycinania wokół tekstu, musimy znać wysokość tekstu (wcześniej tzw. wysokość litery „M” na drukarce) oraz jego szerokość. Możemy użyć parametru ctx.measureText().width, ale parametr ctx.measureText().height nie istnieje.

Na szczęście za pomocą narzędzi CSS (więcej sposobów rozwiązywania problemów ze starszymi implementacjami <canvas> za pomocą pomiarów CSS można znaleźć w sekcji Dane typograficzne) można znaleźć wysokość tekstu, mierząc wartość offsetHeight elementu <span> z tymi samymi właściwościami czcionki:

var d = document.createElement("span");
d.font = "20px arial"
d.textContent = "Hello world!"
var emHeight = d.offsetHeight;

Z tego poziomu możemy utworzyć prostokąt, który posłuży za ścieżkę przycinania, z tym, że będzie zasłaniał „cień”, usuwając fikcyjny kształt.

ctx.rect(0, 0, width, emHeight);
ctx.clip();

Połączenie wszystkich elementów i optymalizacja na bieżąco. Jeśli cień nie ma rozmycia, można zastosować ten sam efekt FillText i nie trzeba będzie konfigurować maski przycinania:

var width = ctx.measureText(text).width;
var style = shadowStyles[text];
// add a background to the current effect
ctx.fillStyle = style.background;
ctx.fillRect(0, offsetY, ctx.canvas.width, textHeight - 1)
// parse text-shadows from css
var shadows = parseShadow(style.shadow);
// loop through the shadow collection
var n = shadows.length; while(n--) {
var shadow = shadows[n];
var totalWidth = width + shadow.blur * 2;
ctx.save();
ctx.beginPath();
ctx.rect(offsetX - shadow.blur, offsetY, offsetX + totalWidth, textHeight);
ctx.clip();
if (shadow.blur) { // just run shadow (clip text)
    ctx.shadowColor = shadow.color;
    ctx.shadowOffsetX = shadow.x + totalWidth;
    ctx.shadowOffsetY = shadow.y;
    ctx.shadowBlur = shadow.blur;
    ctx.fillText(text, -totalWidth + offsetX, offsetY + metrics.top);
} else { // just run pseudo-shadow
    ctx.fillStyle = shadow.color;
    ctx.fillText(text, offsetX + (shadow.x||0), offsetY - (shadow.y||0) + metrics.top);
}
ctx.restore();
}
// drawing the text in the foreground
if (style.color) {
ctx.fillStyle = style.color;
ctx.fillText(text, offsetX, offsetY + metrics.top);
}
// jump to next em-line
ctx.translate(0, textHeight);

Ponieważ tych wszystkich poleceń <canvas> nie da się wpisywać ręcznie, w źródle demonstracyjnym umieściliśmy prosty parser tekstu cienia. Dzięki temu będzie on mógł przekazywać polecenia CSS i generować polecenia <canvas>. Teraz elementy <canvas> mają wiele stylów, z którymi można się łączyć. Tych samych efektów można użyć w przypadku dowolnych obiektów wektorowych, od WebFonts przez złożone kształty zaimportowane z plików SVG po generatywne kształty wektorowe itd.

Cień tekstu w efektach na płótnie

Przerwy (styczne przy wypychaniu piksela)

Pisząc tę sekcję artykułu, zaciekawił mnie przykład stereoskopowy. Jak trudno byłoby stworzyć efekt filmu 3D na ekranie za pomocą <canvas> i 2 zdjęć zrobionych z nieco odmiennych perspektyw? Najwyraźniej niezbyt trudne. To jądro łączy czerwony kanał pierwszego obrazu (dane) z kanałem błękitnym drugiego obrazu (dane2):

data[i] = data[i] * 255 / 0xFF;
data[i+1] = 255 * data2[i+1] / 0xFF;
data[i+2] = 255 * data2[i+2] / 0xFF;

Teraz ktoś musi przykleić taśmą dwa iPhone'y do czoła i jednocześnie kliknąć „Nagraj film”, dzięki czemu będziemy mogli stworzyć własne filmy 3D w HTML5. Jacyś ochotnicy?

okulary 3D

Neonowa tęcza, odbicie zebry – łańcuchy

Łączenie wielu efektów w usłudze <canvas> może być proste, ale wymagana jest podstawowa wiedza o globalCompositeOperation (GCO). Jeśli chcesz porównać operacje z GIMP (lub Photoshopem), w <canvas> jest 12 kodów GCO ciemniejszych, a lżejsze można traktować jako tryby mieszania warstw. Pozostałe operacje są stosowane do warstw jako maski alfa (jedna warstwa usuwa piksele drugiej warstwy). GlobalCompositeoperation łączy „warstwy” (lub w naszym przypadku ciągi kodu) ze sobą, łącząc je w nowe i ekscytujące sposoby:

Grafika z efektami łańcuchów

Wykres globalCompositeOperation pokazuje działanie trybów GCO. Ten wykres wykorzystuje dużą część spektrum kolorów i wielu poziomów przezroczystości alfa, aby dokładnie zobaczyć, czego można się spodziewać. Zalecamy zapoznanie się z globalnymi kompozytorami operacji w Mozilli, w których znajdziesz opisy tekstowe. Więcej informacji na ten temat znajdziesz w opisie Compositing Digital Images autorstwa Portera Duffa.

Mój ulubiony tryb to globalCompositeOperation="lighter". Lżejszy miksuje dołączone piksele, tak jak miksuje światło. Gdy światło czerwone, zielone i białe ma pełną intensywność, widzimy światło białe. To świetna funkcja, którą można wypróbować, zwłaszcza gdy <canvas> ma niski globalny poziom alfa. Umożliwia dokładniejszą kontrolę i płynniejsze krawędzie. Lżejsza funkcja ma wiele zastosowań. Ostatnim moim ulubionym jest tworzenie tła w formacie HTML5 na komputer, które można znaleźć na stronie http://weavesilk.com/. Jedna z moich prezentacji, Oddychanie galaktyk (JS1k), korzysta też z trybu lżejszego – rysując wzory na podstawie tych dwóch przykładów, zaczniecie zobaczyć, jaki efekt daje ten tryb.

Obsługa przeglądarki globalCompositeOperation.

Efekt zakłóceń neonów-tęczy

W poniższej prezentacji uzyskamy efekt neonowy tęczowy (podobny do Photoshopa) i zniekształcony kontur, łącząc różne efekty za pomocą metody globalCompositeOperation (źródłowej, jaśniejszej i ciemniejszej). Ta wersja demonstracyjna jest kontynuacją wersji demonstracyjnej „Cienie tekstu w <canvas>” z zastosowaniem tej samej strategii w oddzielaniu cienia od tekstu (patrz poprzednia sekcja):

Tęczowe zakłócenia
function neonLightEffect() {
var text = "alert('"+String.fromCharCode(0x2665)+"')";
var font = "120px Futura, Helvetica, sans-serif";
var jitter = 25; // the distance of the maximum jitter
var offsetX = 30;
var offsetY = 70;
var blur = getBlurValue(100);
// save state
ctx.save();
ctx.font = font;
// calculate width + height of text-block
var metrics = getMetrics(text, font);
// create clipping mask around text-effect
ctx.rect(offsetX - blur/2, offsetY - blur/2,
        offsetX + metrics.width + blur, metrics.height + blur);
ctx.clip();
// create shadow-blur to mask rainbow onto (since shadowColor doesn't accept gradients)
ctx.save();
ctx.fillStyle = "#fff";
ctx.shadowColor = "rgba(0,0,0,1)";
ctx.shadowOffsetX = metrics.width + blur;
ctx.shadowOffsetY = 0;
ctx.shadowBlur = blur;
ctx.fillText(text, -metrics.width + offsetX - blur, offsetY + metrics.top);
ctx.restore();
// create the rainbow linear-gradient
var gradient = ctx.createLinearGradient(0, 0, metrics.width, 0);
gradient.addColorStop(0, "rgba(255, 0, 0, 1)");
gradient.addColorStop(0.15, "rgba(255, 255, 0, 1)");
gradient.addColorStop(0.3, "rgba(0, 255, 0, 1)");
gradient.addColorStop(0.5, "rgba(0, 255, 255, 1)");
gradient.addColorStop(0.65, "rgba(0, 0, 255, 1)");
gradient.addColorStop(0.8, "rgba(255, 0, 255, 1)");
gradient.addColorStop(1, "rgba(255, 0, 0, 1)");
// change composite so source is applied within the shadow-blur
ctx.globalCompositeOperation = "source-atop";
// apply gradient to shadow-blur
ctx.fillStyle = gradient;
ctx.fillRect(offsetX - jitter/2, offsetY,
            metrics.width + offsetX, metrics.height + offsetY);
// change composite to mix as light
ctx.globalCompositeOperation = "lighter";
// multiply the layer
ctx.globalAlpha = 0.7
ctx.drawImage(ctx.canvas, 0, 0);
ctx.drawImage(ctx.canvas, 0, 0);
ctx.globalAlpha = 1
// draw white-text ontop of glow
ctx.fillStyle = "rgba(255,255,255,0.95)";
ctx.fillText(text, offsetX, offsetY + metrics.top);
// created jittered stroke
ctx.lineWidth = 0.80;
ctx.strokeStyle = "rgba(255,255,255,0.25)";
var i = 10; while(i--) { 
    var left = jitter / 2 - Math.random() * jitter;
    var top = jitter / 2 - Math.random() * jitter;
    ctx.strokeText(text, left + offsetX, top + offsetY + metrics.top);
}    
ctx.strokeStyle = "rgba(0,0,0,0.20)";
ctx.strokeText(text, offsetX, offsetY + metrics.top);
ctx.restore();
};

Efekt odbicia zebry

Efekt „Zebra Reflection” został zainspirowany doskonałym źródłem wskazówek na temat uatrakcyjniania strony za pomocą arkuszy CSS w witrynie WebDesignerWall. To kolejna koncepcja, która tworzy „odbicie” tekstu, podobne do tego, co możesz zobaczyć w iTunes. Efekt łączy właściwości FillColor (białe), createPattern (zebra.png) i linearGradient (shine). Pokazuje to, że do każdego obiektu wektorowego można zastosować wiele typów wypełnienia:

Efekt zebry
function sleekZebraEffect() {
// inspired by - http://www.webdesignerwall.com/demo/css-gradient-text/
var text = "Sleek Zebra...";
var font = "100px Futura, Helvetica, sans-serif";

// save state
ctx.save();
ctx.font = font;

// getMetrics calculates:
// width + height of text-block
// top + middle + bottom baseline
var metrics = getMetrics(text, font);
var offsetRefectionY = -20;
var offsetY = 70;
var offsetX = 60;

// throwing a linear-gradient in to shine up the text
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.1, '#000');
gradient.addColorStop(0.35, '#fff');
gradient.addColorStop(0.65, '#fff');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient
ctx.fillText(text, offsetX, offsetY + metrics.top);

// draw reflected text
ctx.save();
ctx.globalCompositeOperation = "source-over";
ctx.translate(0, metrics.height + offsetRefectionY)
ctx.scale(1, -1);
ctx.font = font;
ctx.fillStyle = "#fff";
ctx.fillText(text, offsetX, -metrics.height - offsetY + metrics.top);
ctx.scale(1, -1);

// cut the gradient out of the reflected text 
ctx.globalCompositeOperation = "destination-out";
var gradient = ctx.createLinearGradient(0, offsetY, 0, metrics.height + offsetY);
gradient.addColorStop(0.0, 'rgba(0,0,0,0.65)');
gradient.addColorStop(1.0, '#000');
ctx.fillStyle = gradient;
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height);

// restore back to original transform state
ctx.restore();

// using source-atop to allow the transparent .png to show through to the gradient
ctx.globalCompositeOperation = "source-atop";

// creating pattern from <image> sourced.
ctx.fillStyle = ctx.createPattern(image, 'repeat');

// fill the height of two em-boxes, to encompass both normal and reflected state
ctx.fillRect(offsetX, offsetY, metrics.width, metrics.height * 2);
ctx.restore();
};

Cienie wewnętrzne i zewnętrzne w Canvas

Specyfikacje <canvas> nie dotyczą cieni „wewnętrznych” i „zewnętrznych”. Przy pierwszym wyświetleniu można spodziewać się, że nie jest obsługiwany. To nieprawda. Włączenie tej opcji jest nieco trudniejsze. Jak zapowiadaliśmy w ostatnim poście z F1LT3R, możesz tworzyć cienie wewnętrzne, stosując unikalne właściwości reguł krętych w prawo i w kierunku przeciwnym do ruchu wskazówek zegara. Aby to zrobić, najpierw narysuj prostokątny kontener, aby utworzyć „cień wewnętrzny”, a następnie, korzystając z przeciwnych, zakręconych reguł, narysuj kształt wycięcia, tworząc odwrotność kształtu.

W przykładzie poniżej można jednocześnie stylizować elementy wewnętrzne i fillStyle za pomocą elementów kolor, gradient i wzór. Możesz określić obrót wzoru osobno. Zwróć uwagę, że pasy zebry są teraz prostopadłe do siebie. Maska przycinania o rozmiarze ramki ograniczającej eliminuje konieczność zamknięcia bardzo dużego kontenera, co przyspieszy pracę, ponieważ zapobiega przetwarzaniu zbędnych części cienia.

Cienie wewnętrzne/zewnętrzne
function innerShadow() {

function drawShape() { // draw anti-clockwise
ctx.arc(0, 0, 100, 0, Math.PI * 2, true); // Outer circle
ctx.moveTo(70, 0);
ctx.arc(0, 0, 70, 0, Math.PI, false); // Mouth
ctx.moveTo(-20, -20);
ctx.arc(30, -30, 10, 0, Math.PI * 2, false); // Left eye
ctx.moveTo(140, 70);
ctx.arc(-20, -30, 10, 0, Math.PI * 2, false); // Right eye
};

var width = 200;
var offset = width + 50;
var innerColor = "rgba(0,0,0,1)";
var outerColor = "rgba(0,0,0,1)";

ctx.translate(150, 170);

// apply inner-shadow
ctx.save();
ctx.fillStyle = "#000";
ctx.shadowColor = innerColor;
ctx.shadowBlur = getBlurValue(120);
ctx.shadowOffsetX = -15;
ctx.shadowOffsetY = 15;

// create clipping path (around blur + shape, preventing outer-rect blurring)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
ctx.clip();

// apply inner-shadow (w/ clockwise vs. anti-clockwise cutout)
ctx.beginPath();
ctx.rect(-offset/2, -offset/2, offset, offset);
drawShape();
ctx.fill();
ctx.restore();

// cutout temporary rectangle used to create inner-shadow
ctx.globalCompositeOperation = "destination-out";
ctx.fill();

// prepare vector paths
ctx.beginPath();
drawShape();

// apply fill-gradient to inner-shadow
ctx.save();
ctx.globalCompositeOperation = "source-in";
var gradient = ctx.createLinearGradient(-offset/2, 0, offset/2, 0);
gradient.addColorStop(0.3, '#ff0');
gradient.addColorStop(0.7, '#f00');
ctx.fillStyle = gradient;
ctx.fill();

// apply fill-pattern to inner-shadow
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 1;
ctx.rotate(0.9);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();

// apply fill-gradient
ctx.save();
ctx.globalCompositeOperation = "destination-over";
var gradient = ctx.createLinearGradient(-offset/2, -offset/2, offset/2, offset/2);
gradient.addColorStop(0.1, '#f00');
gradient.addColorStop(0.5, 'rgba(255,255,0,1)');
gradient.addColorStop(1.0, '#00f');
ctx.fillStyle = gradient
ctx.fill();

// apply fill-pattern
ctx.globalCompositeOperation = "source-atop";
ctx.globalAlpha = 0.2;
ctx.rotate(-0.4);
ctx.fillStyle = ctx.createPattern(image, 'repeat');
ctx.fill();
ctx.restore();

// apply outer-shadow (color-only without temporary layer)
ctx.globalCompositeOperation = "destination-over";
ctx.shadowColor = outerColor;
ctx.shadowBlur = 40;
ctx.shadowOffsetX = 15;
ctx.shadowOffsetY = 10;
ctx.fillStyle = "#fff";
ctx.fill();
};

Na podstawie podanych niżej przykładów widać, że za pomocą metody globalCompositeOperation możemy łączyć efekty łańcuchowe, uzyskując bardziej złożone efekty (z wykorzystaniem maskowania i mieszania). Ekran jest jak Ty ;)

Spaceage – efekty generatywne

W aplikacji <canvas>, przechodząc od znaku Unicode 0x2708:

Unicode gfaphic

...do tego zacienionego przykładu:

Przykład z cieniowaniem

...można to osiągnąć, używając wielu wywołań funkcji ctx.strokeText() o cienkiej liniiSzerokość (0,25), powoli zmniejszając przesunięcie osi x i alfa, dając elementom wektorowym wrażenie ruchu.

Mapując położenie elementów XY na fali sinuso-cosinusowej i zmieniając kolory za pomocą właściwości HSL, możemy uzyskać ciekawsze efekty, np. o zagrożeniu biologicznym:

Efekt kolarstwa HSL

HSL: Odcień, nasycenie, Jasność (1978)

HSL jest nowym formatem w specyfikacjach CSS3. Kod HEX został zaprojektowany z myślą o komputerach, a HSL powinien być czytelny dla człowieka.

Ilustrując łatwość HSL; aby cyklicznie zmieniać spektrum kolorów, zwiększyliśmy po prostu „odcień” z stopnia 360°, a odcień jest mapowany na spektrum w sposób walcowy. Jasność określa, jak bardzo ciemny/jasny jest kolor. 0% oznacza czarny piksel, a 100% – biały. Nasycenie określa, jak jasny i żywy jest kolor. Szare są tworzone z nasyceniem 0%, a żywe – z wartością 100%.

Grafika HSL

HSL to najnowszy standard, więc warto nadal obsługiwać starsze przeglądarki, co jest możliwe dzięki konwersji przestrzeni kolorów. Poniższy kod akceptuje obiekt HSL { H: 360, S: 100, L: 100} i generuje obiekt RGB { R: 255, G: 255, B: 255 }. Następnie możesz użyć tych wartości do utworzenia ciągu RGB lub RGB. Więcej szczegółowych informacji znajdziesz w obszernym artykule w Wikipedii na temat HSL.

// HSL (1978) = H: Hue / S: Saturation / L: Lightness
HSL_RGB = function (o) { // { H: 0-360, S: 0-100, L: 0-100 }
var H = o.H / 360,
    S = o.S / 100,
    L = o.L / 100,
    R, G, B, _1, _2;

function Hue_2_RGB(v1, v2, vH) {
if (vH < 0) vH += 1;
if (vH > 1) vH -= 1;
if ((6 * vH) < 1) return v1 + (v2 - v1) * 6 * vH;
if ((2 * vH) < 1) return v2;
if ((3 * vH) < 2) return v1 + (v2 - v1) * ((2 / 3) - vH) * 6;
return v1;
}

if (S == 0) { // HSL from 0 to 1
R = L * 255;
G = L * 255;
B = L * 255;
} else {
if (L < 0.5) {
    _2 = L * (1 + S);
} else {
    _2 = (L + S) - (S * L);
}
_1 = 2 * L - _2;

R = 255 * Hue_2_RGB(_1, _2, H + (1 / 3));
G = 255 * Hue_2_RGB(_1, _2, H);
B = 255 * Hue_2_RGB(_1, _2, H - (1 / 3));
}

return {
R: R,
G: G,
B: B
};
};

Tworzenie animacji za pomocą metody requestAnimationFrame

Wcześniej dostępne były 2 opcje tworzenia animacji w języku JavaScript: setTimeout i setInterval.

window.requestAnimationFrame to nowy standard, który zastąpi obie te funkcje; oszczędzając światu elektryczność (i kilka uderzeń Twojego komputera) przez umożliwienie przeglądarce regulacji animacji w zależności od dostępnych zasobów. Ważne funkcje:

  • Gdy użytkownik znajdzie ramkę, animacja może się spowolnić lub całkowicie zatrzymać, aby zapobiec wykorzystywaniu niepotrzebnych zasobów.
  • Obowiązuje ograniczenie do 60 FPS. Wynika to z faktu, że obraz jest znacznie powyżej poziomu, który człowiek może zauważyć (większość osób przy 30 FPS widzi animację „płynną”).

W chwili tworzenia tego tekstu musisz używać prefiksu requestAnimationFrame. W ramach funkcji requestAnimationFrame do inteligentnej animacji Paul Ireland utworzył warstwę podkładki z obsługą pomiędzy różnymi sprzedawcami:

// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return  window.requestAnimationFrame       || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame    || 
        window.oRequestAnimationFrame      || 
        window.msRequestAnimationFrame     || 
        function(/* function */ callback, /* DOMElement */ element){
        window.setTimeout(callback, 1000 / 60);
        };
})();

Jeśli pójdziemy dalej, tym ambitniejszym sposobem może być połączenie tego kodu z kodem typu requestAnimationFrame.js (kilka funkcji do opracowania), które będą w większym stopniu obsługiwać starsze przeglądarki przy jednoczesnym przejściu na ten nowy standard.

(function animate() {
var i = 50;
while(i--) {
    if (n > endpos) return;

    n += definition;
    ctx.globalAlpha = (0.5 - (n + startpos) / endpos) * alpha;
    if (doColorCycle) {
        hue = n + color;
        ctx.strokeStyle = "hsl(" + (hue % 360) + ",99%,50%)"; // iterate hue
    }
    var x = cos(n / cosdiv) * n * cosmult; // cosine
    var y = sin(n / sindiv) * n * sinmult; // sin
    ctx.strokeText(text, x + xoffset, y + yoffset); // draw rainbow text
}
timeout = window.requestAnimationFrame(animate, 0);
})();
Grafika rozmycia notatek
Grafika animacji
Grafika matrix

Kod źródłowy

Dzięki obsłudze różnych dostawców przeglądarek nie ma wątpliwości, że usługa <canvas> będzie można przenosić do plików wykonywalnych na iPhone'a, Androida i komputery przy użyciu aplikacji PhoneGap.

Titanium