Canvas vs CSS3: маленький пример

15 июля 2010 г.

Сравнивать CSS3 и canvas — это все равно, что сравнивать декларативную и императивную парадигмы программирования (собственно, они тут и представлены). Но все же именно такое сравнение на простом примере помогает обратить внимание на некоторые архитектурные особенности.

Вот такие CSS3 примеры вы можете видеть на рисунке:

CSS3 примеры


Цели и задачи
Цели исследования были следующими:

  • Сравнить подходы к решению элементарных задач;
  • Указать на недостатки — тут оговорюсь, что недостатками я называю то, что мешает решать данную конкретную задачу, не отрицая возможности эффективного использования той же особенности в других случаях (холивара не будет);
  • Оценить поддержку разными браузерами — мой стенд: Opera 10.52 и Firefox 3.6.3 (устарел уже на момент окончания работы над статьей, но для примера сгодится).

Сравнивалась реализация следующих задач:

  • Нестандартные шрифты (webfonts) — я использовал Lobster и Yanone Kaffeesatz из Google Font Directory;
  • Тень под текстом;
  • Тень под блоками;
  • Закругленные углы рамок.

На рисунке выше обратите внимание на тень под восклицательным знаком а также на то, что позиционирование немного отличается — подозреваю, что последнее связано с использованием разных форматов шрифтов, когда каждый браузер по сути отображает отдельный шрифт.

Реализация


    Сравнение возможностей Canvas и CSS3

<script type="text/javascript">// <![CDATA[
    window.onload = function(e) {
        var canv = document.getElementById('cnv');
        var ctx = canv.getContext('2d');
        ctx.strokeStyle = '#69a';
        ctx.fillStyle = '#def';
        ctx.lineWidth = 8;
        ctx.shadowColor = '#aaa';
        ctx.shadowOffsetX = 2;
        ctx.shadowOffsetY = 2;
        ctx.shadowBlur = 6;

        //bg
        ctx.beginPath();
        ctx.moveTo(30,12);
        ctx.lineTo(440,12);
        ctx.quadraticCurveTo(460,12,460,32);
        ctx.lineTo(460,270);
        ctx.quadraticCurveTo(460,290,440,290);
        ctx.lineTo(30,290);
        ctx.quadraticCurveTo(10,290,10,270);
        ctx.lineTo(10,32);
        ctx.quadraticCurveTo(10,12,30,12);
        //shadow trick
        ctx.stroke(); //now we have shadow
        ctx.shadowColor = 'transparent'; //turn shadow off
        ctx.fill(); //fill, no shadow
        ctx.stroke(); //stroke again, no shadow

        //text+shadow
        ctx.shadowBlur = 4;
        ctx.shadowColor = '#223338';
        ctx.fillStyle = '#69a';
        ctx.textBaseline = 'bottom';
        ctx.font = 'normal 94px "Lobster", arial, serif';
        ctx.fillText('I can do', 64, 140);
        ctx.font = 'normal 106px "Lobster", arial, serif';
        ctx.fillText('canvas', 64, 238);
        ctx.font = 'normal 210px "Lobster", arial, serif';
        ctx.fillText('!', 358, 260);

        //text+rotation
        ctx.font = 'normal 28px/28px "Yanone Kaffeesatz", arial, serif';
        ctx.fillStyle = '#b88';
        ctx.shadowColor = 'transparent';
        ctx.rotate(-4*2*Math.PI/360);
        ctx.fillText('Kneel before HTML5', 230, 280);
    };

// ]]></script></pre>
<div id="css-wrapper">
<div class="line1">I can do</div>
<div class="line2">styles<span>!</span></div>
<div class="line3">Kneel before CSS3</div>
</div>
<div id="canvas-wrapper"><canvas id="cnv" width="500" height="300">
 Здесь была эротическая картинка, но ваш браузер не может canvas :(
 Установите хороший браузер и наслаждайтесь!
 </canvas></div>
<pre>

Уже при беглом взгляде на исходный код видно, что оба решения примерно одинаковы по количеству строк, причем каждое из них страдает от своей неуклюжести. В стилях приходится дублировать свойства с префиксами для разных движков (причем меньше двух повторов никак не получается, при условии работы хотя бы в двух браузерах), а в коде для canvas приходится изобретать четырехколесный велосипед, чтобы просто закруглить углы:
ctx.beginPath();

ctx.moveTo(30,12);

ctx.lineTo(440,12);

ctx.quadraticCurveTo(460,12,460,32);

ctx.lineTo(460,270);

ctx.quadraticCurveTo(460,290,440,290);

ctx.lineTo(30,290);

ctx.quadraticCurveTo(10,290,10,270);

ctx.lineTo(10,32);

ctx.quadraticCurveTo(10,12,30,12);


Дальше — больше. Вот так из-за отсутствия функции strokeAndFill() (или fillAndStroke()?) рисуется тень:
ctx.shadowColor = '#aaa'; //цвет тени

ctx.shadowOffsetX = 2;

ctx.shadowOffsetY = 2;

ctx.shadowBlur = 6;

ctx.stroke(); //рисуем рамку с тенью

ctx.shadowColor = ‘transparent’; //выключаем тень

ctx.fill(); //закрашиваем тень внутри рамки

ctx.stroke(); //рисуем рамку снова, поверх заливки

Недостатки CSS3
Могу выделить следующие моменты:

  • Многословность — количество свойств на один селектор даже для простых примеров довольно большое, поэтому лучше бы иметь более легковесный синтаксис для всего этого. Решение: Sass;
  • Дублирование свойств с префиксами для разных движков. Решение: eCSStender;
  • Невозможность узнать, поддерживается ли определенное свойство. Решение: Modernizr

Недостатки HTML5 canvas
Об этом можно поспорить всерьез и надолго, но рискну их назвать:

  • Скудный набор инструментов для работы с тектом. По сути, самой интересной особенностью является рисование контуров глифов с помощью strokeText(). Не хватает подгона размера под заданную ширину и высоту;
  • Скудный набор примитивов — вот это весьма спорный момент, минимализм не является недостатком (скорее наоборот), но в данном конкретном примере это создало излишние трудности;
  • Нет функции strokeAndFill(), что затрудняет работу с тенями;
  • Нет понятия графических объектов или групп операций, все возлагается на разработчика.

Все вышеназванные трудности решаются при помощи библиотек — конкретных наименований не знаю, но могу привести в пример processing.js.

Внешний вид в разных движках
Тут ситуация довольно плачевная. Помимо обрезаных теней есть общий недостаток Firefox и Opera: достаточно темная тень под закругленным углом имеет неприятный артефакт в виде окантовки закругленного угла полоской светлых пикселей — попробуйте поменять цвет тени под блоком на более темный, чтобы увидеть.

Интересно то, что и Opera, и Firefox портят тени по одинаковой схеме, рисуя их только в clipping region’е символов и игнорируя реальную форму глифов, но Opera делает это для CSS, а Firefox — для canvas. Это наглядный пример типичной ошибки разработчиков.

Ситуацию с разным позиционированием я пояснил выше, но это только мое предположение.

Выводы
Все плохо, ничего не работает :)

Если серьезно, то CSS3 примеры показывают, что на данный момент действительно не все гладко, причем замечание это касается не только неполной поддержки в браузерах, но и некоторыми архитектурными особенностями. Благо, решения многих проблем уже имеются.

Вопрос об IE умалчивается сознательно.

Ссылки

Теги:
рубрика Сайтостроение