캔버스의 활자 효과

Michael 특가
Michael 특가

내 배경

2006년에 Firefox v2.0이 출시되었을 때 <canvas>에 대해 인식하게 되었습니다. 변환 매트릭스를 설명하는 Ajaxian 관련 도움말을 보고 저의 첫 번째 <canvas> 웹 앱인 Color Sphere (2007)를 만들었습니다. 그 결과 색상의 세계에 몰입하게 되었고 그래픽 프리미티브에 Sketchpad (2007-2008)를 만들었습니다. 이는 브라우저에서 '그림판'보다 낫습니다. 이러한 실험을 통해 오랜 친구인 Charles Pritchard와 함께 Mugtug라는 스타트업이 설립되었습니다. Google에서는 HTML5 <canvas>암실을 개발 중입니다. 암실은 비파괴 사진 공유 앱으로, 픽셀 기반 필터와 벡터 기반 서체 및 그림의 이점을 결합합니다.

소개

캔버스 배너 그래픽

<canvas>를 사용하면 자바스크립트 프로그래머가 화면의 색상, 벡터, 픽셀, 즉 모니터의 시각적 구성을 완전히 제어할 수 있습니다.

다음 예에서는 <canvas>에서 그다지 주목받지 못한 한 영역, 즉 텍스트 효과를 만듭니다. <canvas>에서 만들 수 있는 다양한 텍스트 효과는 상상할 수 있는 만큼 방대합니다. 이 데모에서는 가능성의 하위 섹션을 다룹니다. 이 문서에서는 '텍스트'를 다루지만 이 메서드는 모든 벡터 객체에 적용할 수 있습니다. 즉, 게임과 기타 애플리케이션에서 흥미로운 시각 효과를 만들 수 있습니다.

<canvas>의 텍스트 그림자
<canvas>CSS와 유사한 텍스트 효과: 클리핑 마스크를 만들고 <canvas>에서 측정항목을 찾고 그림자 속성을 사용합니다.
네온 무지개, 얼룩말 반사 - 연쇄 효과
globalCompositeOperation, createLinearGradient, createPattern을 사용하는 <canvas> 예시에서 Photoshop과 유사한 텍스트 효과입니다.
<canvas>의 내부 및 외부 그림자
약간 알려진 특징을 보여줍니다. 시계 방향과 반시계 방향 와인딩을 사용하여 그림자 (내부 그림자)의 역을 만듭니다.
Spaceage - 생성 효과.
hsl() 색상 순환과 window.requestAnimationFrame를 사용하여 <canvas>생성형 기반 텍스트 효과로 움직임의 느낌을 줍니다.

캔버스의 텍스트 그림자

CSS3 사양에서 테두리 반경, 웹 그라데이션 등과 함께 가장 마음에 드는 추가 기능 중 하나는 그림자를 만드는 기능입니다. 특히 다음과 같이 CSS와 <canvas> 그림자의 차이점을 인식하는 것이 중요합니다.

CSS는 두 가지 메서드, 즉 div, span 등의 상자 요소에 box-shadow를, 텍스트 콘텐츠에 text-shadow를 사용합니다.

<canvas>에는 그림자 유형이 한 가지 있습니다. ctx.moveTo, ctx.lineTo, ctx.bezierCurveTo, ctx.quadradicCurveTo, ctx.arc, ctx.rect, ctx.fillText, ctx.strokeText 등 모든 벡터 객체에 사용됩니다. <canvas>에서 그림자를 만들려면 다음 네 가지 속성을 탭합니다.

ctx.shadowColor = "빨간색" // 문자열
그림자의 색상: RGB, RGBA, HSL, HEX 및 기타 입력이 유효합니다.
ctx.shadowOffsetX = 0; // 정수
텍스트를 기준으로 한 그림자의 가로 거리입니다.
ctx.shadowOffsetY = 0; // 정수
텍스트를 기준으로 한 그림자의 세로 거리입니다.
ctx.shadowBlur = 10; // 정수
음영 블러 효과는 값이 클수록 블러가 커집니다.

시작하기 위해 <canvas>가 CSS 효과를 에뮬레이션하는 방법을 살펴보겠습니다. Google 이미지에서 'css text-shadow'를 검색하면 Line25, Stereoscopic, Shadow 3D 등 몇 가지 훌륭한 데모를 에뮬레이션할 수 있습니다.

CSS 3D 그래픽

입체 3D 효과 (자세한 내용은 애너글리프 이미지 참고)는 간단한 코드 줄의 예로, 유용하게 활용할 수 있습니다. 다음 CSS 라인을 사용하면 3D 영화에서 제공하는 3D 빨간색/녹청색 유리로 볼 때 깊이가 있는 듯한 착시를 일으킬 수 있습니다.

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

이 문자열을 <canvas>로 변환할 때 다음 두 가지 사항에 유의하세요.

  1. 그림자 블러 (세 번째 값)가 없으므로 fillText가 동일한 결과를 생성하므로 실제로 그림자를 실행할 이유가 없습니다.
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. EM은 <canvas>에서 지원되지 않으므로 PX로 변환해야 합니다. DOM에서 동일한 글꼴 속성이 있는 요소를 만들고 측정할 형식으로 너비를 설정하여 PT, PC, EM, EX, PX 및
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>

알파 곱셈 방지

Line25의 네온 효과와 같은 좀 더 복잡한 예에서는 효과를 올바르게 에뮬레이션하기 위해 shadowBlur 속성을 사용해야 합니다. 네온 효과는 여러 그림자에 의존하므로 문제가 발생합니다. <canvas>에서 각 벡터 객체에는 그림자가 하나만 있을 수 있습니다. 따라서 여러 개의 그림자를 그리려면 그 위에 여러 버전의 텍스트를 그려야 합니다. 이로 인해 알파 곱셈이 발생하고 결국 가장자리가 들쑥날쑥해집니다.

네온 그래픽

ctx.fillStyle = "rgba(0,0,0,0)" 또는 "transparent"를 실행하여 그림자를 표시하면서 텍스트를 숨기려고 했습니다. 하지만 이 시도는 소용이 없었습니다. 그림자는 fillStyle 알파의 곱셈이므로 그림자는 fillStyle보다 더 불투명할 수 없습니다.

다행히 이 문제를 피하는 방법이 있습니다. 텍스트에서 그림자 오프셋을 그려서 텍스트를 분리한 상태로 유지 (겹치지 않도록)하여 화면 측면에 텍스트를 숨길 수 있습니다.

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);

텍스트 블록 주변 자르기

이 문제를 해결하기 위해 클리핑 경로를 추가하여 처음에 fillText가 그려지지 않도록 할 수 있습니다 (그림자 그리기는 허용). 텍스트를 둘러싸는 클리핑 경로를 만들려면 텍스트의 높이(이전에는 'em-height'라고 함)(이전에는 인쇄판의 문자 'M' 높이)와 텍스트의 너비를 알아야 합니다. ctx.measureText().width를 사용하여 너비를 가져올 수 있지만 ctx.measureText().height는 존재하지 않습니다.

다행히 CSS 해킹을 통해 (CSS 측정을 사용하여 <canvas>의 이전 구현을 수정하는 더 많은 방법은 타이포그래피 측정항목 참고) 동일한 글꼴 속성으로 <span>offsetHeight를 측정하여 텍스트의 높이를 찾을 수 있습니다.

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

여기에서 클리핑 경로로 사용할 직사각형을 만들 수 있습니다. 즉, 더미 모양을 삭제하는 동안 '그림자'를 둘러쌉니다.

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

모두 하나로 묶고 진행하면서 최적화 - 그림자에 블러가 없으면 fillText를 동일한 효과에 사용할 수 있으므로 클리핑 마스크를 설정하지 않아도 됩니다.

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);

이러한 모든 <canvas> 명령어를 수동으로 입력하지 않으려고 하므로 데모 소스에 간단한 텍스트 그림자 파서를 포함했습니다. 이렇게 하면 CSS 명령어를 입력하여 <canvas> 명령어를 생성할 수 있습니다. 이제 <canvas> 요소에는 연결할 수 있는 다양한 스타일이 있습니다. 동일한 그림자 효과를 WebFonts에서 SVG에서 가져온 복잡한 모양, 생성적 벡터 도형에 이르기까지 모든 벡터 객체에서 사용할 수 있습니다.

캔버스 효과의 텍스트 그림자

중단 (픽셀 푸시 시 탄젠트)

기사의 이 섹션을 작성하면서 입체적인 예시를 보고 호기심이 생겨났습니다. <canvas> 및 약간 다른 관점에서 찍은 두 개의 이미지를 사용하여 3D 영화 화면 효과를 만드는 것은 얼마나 어려운가요? 그다지 어렵지는 않은 것 같네요. 다음 커널은 첫 번째 이미지 (data)의 빨간색 채널과 두 번째 이미지 (data2)의 청록색 채널을 결합합니다.

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

이제 누군가가 이마에 iPhone 두 대를 덕트 테이프로 붙이고 '동영상 녹화'를 클릭하기만 하면 HTML5로 직접 3D 영화를 만들 수 있습니다. 자원자가 있나요?

3D 안경

네온 무지개, 얼룩말 반사—체인 효과

<canvas>에서 여러 효과를 체이닝하는 것은 간단할 수 있지만, globalCompositeOperation (GCO)에 관한 기본적인 지식이 필요합니다. 작업을 GIMP (또는 Photoshop)와 비교하면 <canvas> 더 어두운 GCO가 12개 있고 밝은 작업은 레이어 혼합 모드로 생각할 수 있습니다. 다른 10개 작업은 레이어에 알파 마스크로 적용됩니다 (한 레이어는 다른 레이어의 픽셀을 삭제함). globalCompositeOperation은 '레이어'(여기서는 코드 문자열)를 연결하여 새롭고 흥미로운 방식으로 결합합니다.

효과 그래픽 체이닝

globalCompositeOperation 차트에서는 작동 중인 GCO 모드를 보여줍니다. 이 차트에서는 예상되는 결과를 자세히 보기 위해 많은 부분의 색상 스펙트럼과 여러 수준의 알파 투명도를 사용합니다. Mozilla 의 globalCompositeOperation 참조에서 텍스트 설명을 확인해 보시기 바랍니다. 자세한 내용은 포터 더프의 디지털 이미지 합성에서 이 작업이 어떻게 작동하는지 알아보세요.

제가 가장 좋아하는 모드는 globalCompositeOperation="lighter"입니다. 밝은 테마는 빛이 혼합되는 방식과 유사하게 추가된 픽셀을 혼합합니다. 빨간색, 녹색, 흰색 빛이 최대 강도일 때는 백색광이 보입니다. 특히 <canvas>가 낮은 글로벌 알파로 설정된 경우 이 기능을 사용하면 더 세밀하게 제어할 수 있고 더 부드러운 가장자리를 사용할 수 있습니다. 라이터는 여러 용도로 사용되었는데, 최근에는 http://weavesilk.com/에서 HTML5 데스크톱 배경화면을 제작하는 것을 좋아했습니다. 제 데모 중 하나인 Breathing Galaxies (JS1k)에서도 더 밝은 모드를 사용합니다. 이 두 가지 예에서 패턴을 그리면 이 모드가 어떤 영향을 미치는지 확인할 수 있습니다.

globalCompositeOperation 브라우저 처리.

네온-무지개 잡음 효과

다음 데모에서는 globalCompositeOperation (소스-인, 라이트, 더 어둡게)을 사용하여 효과를 함께 연결하여 잡음이 있는 윤곽선의 포토샵과 유사한 네온 무지개 발광 효과를 구현합니다. 이 데모는 '<canvas>의 텍스트 그림자' 데모의 진행으로, 동일한 전략을 사용하여 그림자를 텍스트와 분리합니다 (이전 섹션 참고).

무지개 잡음
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();
};

얼룩말 반사 효과

Zebra Reflection 효과는 CSS로 페이지를 꾸미는 방법에 관한 WebDesignerWall의 유용한 리소스에서 영감을 받았습니다. 여기서 아이디어를 좀 더 살펴보면 iTunes에서 볼 수 있는 것과 같이 텍스트에 대한 '반사'를 만들 수 있습니다. 이 효과는 fillColor (흰색), createPattern (zebra.png), linearGradient (shine)를 결합합니다. 이는 각 벡터 객체에 여러 채우기 유형을 적용하는 기능을 보여줍니다.

얼룩말 효과
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();
};

캔버스의 내부/외부 그림자

<canvas> 사양은 '내부' 그림자와 '외부' 그림자의 주제를 다루지 않습니다. 실제로 처음 나타날 때는 '내부' 그림자가 지원되지 않을 것으로 예상할 수 있습니다. 호출은 건너뛸 수 없습니다. 사용 설정하기가 좀 더 까다롭습니다.) F1LT3R의 최근 게시물에서 제안한 것처럼 시계 방향과 반시계 방향 와인딩 규칙의 고유한 속성을 사용하여 내부 그림자를 만들 수 있습니다. 이렇게 하려면 컨테이너 직사각형을 그려 '내부 그림자'를 만든 후 반대 권선 규칙을 사용하여 컷아웃 도형을 그려서 도형의 역을 만듭니다.

다음 예에서는 내부 그림자와 fillStyle을 색상+그라데이션+패턴으로 동시에 스타일을 지정할 수 있습니다. 패턴 회전을 개별적으로 지정할 수 있습니다. 이제 얼룩말 줄무늬가 서로 수직을 이룹니다. 경계 상자 크기의 클리핑 마스크를 사용하면 컷아웃 형태를 둘러싸는 초대형 컨테이너가 필요하지 않습니다. 이는 그림자의 불필요한 부분이 처리되지 않도록 하여 속도를 개선합니다.

내부/외부 그림자
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();
};

이러한 예제에서 globalCompositeOperation을 사용하면 마스킹 및 블렌딩을 활용하여 보다 정교한 효과를 생성하여 함께 체인 효과를 만들 수 있습니다. 화면이 당신의 굴입니다 ;)

Spaceage - 생성 효과

<canvas>에서 유니코드 문자 0x2708부터 다음을 실행합니다.

유니코드 gfaphic

음영 처리된 다음 예시로 바꾸겠습니다.

음영 처리된 예

x-오프셋과 알파를 천천히 줄이면서 얇은 lineWidth (0.25)로 ctx.strokeText()를 여러 번 호출하여 벡터 요소에 움직임을 줄 수 있습니다.

요소 XY 위치를 사인파/코사인파에 매핑하고 HSL 속성을 사용하여 색상을 순환하여 다음과 같은 '생물학적 위험' 예시와 같은 좀 더 흥미로운 효과를 만들 수 있습니다.

HSL 사이클 효과

HSL: 색조, 채도, 밝기 (1978)

HSL은 CSS3 사양에서 새로 지원되는 형식입니다. HEX가 컴퓨터용으로 설계된 반면 HSL은 사람이 읽을 수 있도록 설계되었습니다.

HSL의 편의성을 보여줍니다. 색상 스펙트럼을 순환하려면 단순히 '색조'를 360에서 증가시킵니다. 색조는 원통형 방식으로 스펙트럼에 매핑됩니다. 밝기는 색상이 얼마나 어둡거나 밝은지 제어합니다. 0% 는 검은색 픽셀을 나타내고 100% 는 흰색 픽셀을 나타냅니다. 채도는 색상이 얼마나 밝거나 선명한지 제어합니다. 회색은 채도 0%로 만들고 선명한 색상은 100% 값을 사용하여 만듭니다.

HSL 그래픽

HSL은 최신 표준이므로 색상 공간 변환을 통해 가능한 이전 브라우저를 계속 지원하는 것이 좋습니다. 다음 코드는 HSL 객체 { H: 360, S: 100, L: 100}을 허용하고 RGB 객체 { R: 255, G: 255, B: 255 }를 출력합니다. 여기에서 이 값을 사용하여 rgb 또는 rgba 문자열을 만들 수 있습니다. 자세한 내용은 HSL에 대한 Wikipedia의 유용한 문서를 참조하세요.

// 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
};
};

requestAnimationFrame으로 애니메이션 만들기

이전에는 setTimeoutsetInterval, 이렇게 두 가지 방법으로 JavaScript로 애니메이션을 만들 수 있었습니다.

window.requestAnimationFrame는 두 가지를 모두 대체하는 새로운 표준입니다. 브라우저가 사용 가능한 리소스를 기반으로 애니메이션을 규제할 수 있도록 하여 세계 전력 (및 컴퓨터의 심장 박동)을 절약할 수 있습니다. 몇 가지 중요한 기능은 다음과 같습니다.

  • 사용자가 프레임에 존재하면 애니메이션이 느려지거나 완전히 중지되어 불필요한 리소스의 사용을 방지할 수 있습니다.
  • 60FPS의 프레임 속도에는 제한이 있습니다. 그 이유는 인간이 인식할 수 있는 수준보다 훨씬 높기 때문입니다 (대부분 사람이 30FPS로 애니메이션을 '유동적으로 표시').

이 문서 작성 시점에 requestAnimationFrame를 사용하려면 공급업체별 접두사가 필요합니다. 폴 아이리시는 스마트 애니메이션을 위한 requestAnimationFrame에서 크로스 벤더를 지원하는 shim 레이어를 만들었습니다.

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

좀 더 나아가 이 새로운 표준으로 전환하면서 이전 브라우저를 더 광범위하게 지원하는 requestAnimationFrame.js(해결해야 할 몇 가지 기능이 있음)와 같은 폴리필과 연결할 수도 있습니다.

(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);
})();
메모 흐리게 처리 그래픽
애니메이션 그래픽
매트릭스 그래픽

소스 코드

브라우저 공급업체 영역 전반에서 지원되므로 PhoneGap을 사용하여 iPhone/Android/데스크톱 실행 파일로 포팅할 수 있는 <canvas>의 미래에 대해서는 의문할 필요가 없습니다.

티타늄.