Наступает Новый год!
Хорошо бы поднять настроение себе и другим.
Копировать и рассылать открыточки с красивыми картинками и гифками, скаченными и
сети, уже как-то не интересно.
Хочется чего-то необычного, яркого, праздничного, которое есть только у тебя и которо
можно подарить близкому человеку со словами,- "Я сделал это для тебя!"
или оставить у себя с чувством удовлетворения и гордости :-)
Идея сделать новогодний конкурс анимаций принадлежит @Hamster
Вот и рисунок, который она дала для образца.
Смотрите, как и в любой другой новогодней открытке, тут есть потенциал для всевозможны
анимаций.
Например:
Лучи от звезды.
Можно легко сделать с помощью stroke-dasharray для широкой строки. Добавить радиальны
градиент, анимацию вращения строки и перемещения градиента.
Снежинки
Нарисовать в CSS или в SVG многие смогут и далее за анимировать размеры, вращение
падение снежинок.
Елочка
Перемигивание гирлянд, легкое покачивание и кручение елочных игрушек, бенгальски
огни и т.д и т.п
Анимация фразы - С Новым 2019 годом!
движение фразы и/или покачивание, обводка, появление букв
Анимация новогодних персонажей,- белочки, зайчики :) Дед мороз и
снегурочка
Можно делать весь вышеперечисленный список анимаций, можно реализовать один пунк
или несколько.
Главное условие - картинки можно брать любые или рисовать свои, но анимация должн
быть своя, никакого копи-паста!
Конкурс завершился.
Поздравляю победителя @Misha Saidov
Благодаря вашим работам дорогие участники, конкурс получился интересным, ярким, запоминающимся
Мы вместе узнали имена новых участников, которые сразу, с ходу ворвались в конкурс
сделали очень крутые работы.
Все работы сделаны на высоком уровне и каждая по своему интересна и хороша.
Пришлось обратится за помощью, но потом и меня осенила собственная идея:
pug:
-var x = 7
.tree
- for (var i = 0; i < x; i++)
.x
.inc
- for (var a = 0; a < i; a++)
svg
.mid
svg
.dec
- for (var b = i; b < 2*i; b++)
svg
scss:
.inc {
flex-flow: row-reverse nowrap;
}
@for $i from 1 through $n/2 {
.tree svg:nth-of-type(#{$i}) {
width: $w/$n/$i + 0px;
height: $w/$n/$i + 0px;
}
}
Получилось не так изящно. Смысл в том, что создаю строки .x, в каждой строке 3 блок
.inc (по возрастанию), .dec (по убыванию для его реализации в стилях задаю flex-row: row-reverse) и .mid (серединка).
ЗАДАЧА 2. СНЕГ
Признаюсь честно, за основу был взят плагин magic-snowflakes, но код js преобразован в scss:
pug:
.snowflakes
-var snow = 15
- for (var i = 0; i < snow; i++)
span
i
scss:
@for $i from 1 through $snow-n {
.snowflakes span:nth-of-type(#{$i}) {
animation-delay: random(1) + 0s;
animation-duration: randomNum(10, 30) + 0s;
left: random(100) + 0%;
margin-top: -1*random(1)*$snow-w + 0px;
width: $snow-w + 0px;
height: $snow-w + 0px;
z-index: 10099;
opacity: random(1);
i {
animation-name: snowflake_x_#{$i};
animation-delay: random(1) + 0s;
}
}
}
codepen
ЗАДАЧА 3. НАДПИСЬ (Handwriting)
За пример были взяты работы с codepen: пример1,пример2. Перепробовав все возможные варианты:
Открываем adobe illustrator
Пишем любой текст, задаем необходимый шрифт, размер
Выбираем слой текст и жмем: CTRL+SHIFT+O или Type -> Create outlines
Это все будет наш clip-path. Блокируем этот слой.
Создаем новый слой или группу слоев.
Выбираем карандаш и аккуратно (на сколько это возможно) прорисовываем каждую букву по отдельности.
Это будет наш stroke который мы будем анимировать.
Сохраняем в svg вставляем в html(pug) код.
Анимация заключается в изменении значения stroke-dashoffset:
path {
stroke-width: 7;
animation: dash 8s linear forwards infinite;
stroke-dasharray: 2200;
stroke-dashoffset: 2200;
}
@keyframes dash {
0% {
stroke-dashoffset: 2200;
}
100% {
stroke-dashoffset: 0;
}
}
При этом важно задать stroke-width такой, чтобы полностью заполнить букву.
Получаем: codepen
P.S: буквы обрисовала не аккуратно, поэтому есть заметные изъяны).
Итого:
🎄 ПРИМЕР НА CODEPEN.
❄ ❄ ❄ ❄ ❄ ❄ ❄
С наступающим Новым Годом! Мира всем и добра!
❄ ❄ ❄ ❄ ❄ ❄ ❄
Ответ 2
Это бандл из моих предыдущих ответов.
Как видите, код стал модульным - можно на его базе собрать любого Франкенштейна
Также код вариативный везде, где только можно - любой коэффициент можно поменять, чтоб
добиться желаемого результата и резиновый (на сколько смог). Повторюсь, что я не умею в векторную графику и канвас, поэтому накодил все на дивах и картинках. Разверните на всю страницу, если сильно сплющит.
UPD 0: Добавил покачивание снежинок, изменил некоторые коэффициенты и сменил изображение звездочек елки на более нейтральное.
UPD 1: Добавил текст и заанимировал.
UPD 2: Санта! Он будет появляться и махать рукой с разных сторон экрана. Думаю, далее будут только какие-то минорные правки, а так - это финалочка ;)
UPD 3: Оптимизация. Теперь все делает один цикл (вместо 4-х, как раньше). Открытк
стала чуть более снисходительна на старт: картинки появляются только после полной загрузки и убрал ненужный тут $(document).ready() Скрипт по-прежнему костыль :)
const randomBetween = (a, b) => {
return (a + (Math.random() * (b - a)));
}
const randomArr = (arr) => {
return arr[Math.round(Math.random() * (arr.length - 1))];
}
const params = {
snowflakes: {
amount: 15,
duration: {
min: 3,
max: 8
},
size: {
min: 10,
max: 32
},
src: [
"http://pngimg.com/uploads/snowflakes/snowflakes_PNG7578.png",
"https://vignette.wikia.nocookie.net/fantendo/images/2/27/Snowflake.png/revision/latest?cb=20121220012520",
"https://latxikadelacerveza.es/wp-content/plugins/christmas-panda/assets/images/snowflake_5.png"
],
container: "#snowflakes"
},
tree: {
amount: 300,
stretching: 1.4,
movement: 5,
src: "https://i.imgur.com/pPjPR8q.png",
duration: {
min: 5,
max: 50
},
size: {
min: 20,
max: 30
},
container: "#tree"
},
rays: {
amount: 40,
duration: 30,
width: 3,
height: 100,
perspective: 150,
color: "rgba(215,249,111,1)",
container: "#rays"
},
text: {
text: "С Новым 2019 годом!",
duration: 30,
width: 3,
height: 100,
perspective: 150,
color: "rgba(215,249,111,1)",
container: "#text"
}
}
let totalAmount = 0;
for (let key in params) {
let amount = params[key].amount;
totalAmount += typeof amount === "number" ? amount : 0;
}
for (let i = 0; i < totalAmount; i++) {
if (i < params.snowflakes.amount) {
let snowflake = $("");
let randomSize = randomBetween(params.snowflakes.size.min, params.snowflakes.size.max);
snowflake.css({
"width": randomSize + "px",
"height": randomSize + "px",
"left": randomBetween(0, 100) + "%",
"animation-duration": randomBetween(params.snowflakes.duration.min, params.snowflakes.duration.max) + "s"
});
$(params.snowflakes.container).append(snowflake);
}
if (i < params.tree.amount) {
if (i === 0) {
$(params.tree.container).append("");
}
let star = $("");
let top = randomBetween(0, randomBetween(70, 100));
let randomSize = (randomBetween(params.tree.size.min, params.tree.size.max) * (top + 10) / 100);
star.css({
"width": randomSize + "px",
"height": randomSize + "px",
"left": "calc(50% - " + (Math.sin(top / params.tree.stretching) * top) + "px)",
"top": top + "%",
"animation-duration": randomBetween(params.tree.duration.min, params.tree.duration.max) + "s",
"transform-origin": (50 + randomBetween(-params.tree.movement, params.tree.movement)) + "% " + (50 + randomBetween(-params.tree.movement, params.tree.movement)) + "%"
});
$(params.tree.container).append(star);
}
if (i < params.rays.amount) {
if (i === 0) {
$(params.rays.container).css({
"animation-duration": params.rays.duration + "s",
width: params.rays.height * 2 + "px",
height: params.rays.height * 2 + "px"
});
window.alignRays = () => {
let mainStar = $(params.tree.container).find(".main");
$(params.rays.container).css("top", (mainStar.offset().top - params.rays.height) + (mainStar.height() / 2) + "px");
}
$(window).on("resize", window.alignRays);
}
let ray = $("");
ray.css({
"width": params.rays.width + "px",
"height": params.rays.height + "px",
"transform": "perspective(" + params.rays.perspective + "px) rotateZ(" + (360 / params.rays.amount * i) + "deg) rotateX(-60deg)",
"background": "linear-gradient(to bottom, rgba(0,0,0,0) 0%," + params.rays.color + " 100%)"
});
$(params.rays.container).append(ray);
}
if (i < params.text.text.length) {
let char = params.text.text.substr(i, 1);
char = char === " " ? " " : char;
let charElem = $("
Всех с наступающим!!
Сцена создана без использования каких-либо ресурсов. Только математика.
Запаситесь видеокартой, тут процессор не делает почти совсем ничего, фрагментный шейдер тут больше чем в 400 строчек получился =)
Это все придумано давным давно, однако раньше, трассировка лучей в реальном времен
была фантастикой и таким образом создавали статические картинки. сегодня каждый обладатель компьютера с видеокартой может написать фрагментный шейдер, который делает трассировку простеньких (и не очень) сцен в реальном времени.
Для этого потребуются:
Желание разбираться с математикой
Много свободного времени
Видеокарта, у меня gtx1050ti (768 вычислительных ядер) и это мало :(
Браузер с поддержкой WebGL
Во фрагменте кода приведена уже склеенная версия, исходник на github gists и blocks.
Итак, попробую по-простому описать что тут происходит, т.к. код вы можете и сам
почитать.
Для начала WebGL инициализируется таким образом, чтобы нарисовать один треугольник
который покрывает весь экран как на изображении ниже, или как-то иначе, главное вызвать фрагментный шейдер для каждого пикселя.
Для этого я как-то написал себе маленькую утилиту GLx, которая мне помогает сделать это в несколько строк.
Фрагментный шейдер делает всю работу.
Перед формированием каждого кадра, из javascript в шейдер передаются некоторые переменные
которые характеризуют сцену, в нашей ёлочке это положение наблюдателя в декартовых координатах, размер картинки, и еще для анимации понадобится время.
конечно перед каждым кадром обновлять все uniform переменные не нужно, но в это
примере я сделал так для простоты кода.
Далее в каждом фрагменте(пикселе) происходит трассировка луча
В нашем случае это raymarching, что про сути есть оптимизация шага трассировки.
В отличие от классического представления объектов сцены в виде треугольников, ту
используются математические формулы дистанции до поверхностей. Шагая вдоль луча, применяетс
огромная формула (функция map(vec3)), которая вычисляет эту дистанцию до всех объектов в сцене. Дистанция которую вернула эта функция - безопасное расстояние, шагнув на которое, мы точно не во что не упремся. Собственно это будет следующий шаг, и так до тех пор, пока луч не упрется в поверхность.
vec2 map(vec3 p) {
vec2 res = vec2(VERY_FAR, -1);
res = opU( res, snow(p) );
res = opU( res, snowmans_accesories(p) );
res = opU( res, tree(p) );
return res;
}
изначально результат инициализируется расстоянием очень далеким от наблюдателя, т.е. будущим фоном. -1 выступает в роли идентификатора материала.
функция opU - названа от слов operation и union и предназначена для объединения объектов сцены.
каждый компонент, объединяемый этой операцией это в свою очередь тоже функция, тольк
описывающая один объект сцены плюс возвращающая вторым компонентом идентификатор(цвет
материала в найденной точке. в свою очередь состят из более простых примитив, сфер, цилиндров, кубов, фракталов, поверхностей безье, чего душе угодно, между которыми возможно всевозможные трансформации, булевые операции итд.
все вместе это и описывает нашу сцену с елочкой.
после того, как точка пересечения луча с поверхностью найдена, начинается самое сложное и самое интересное, это определение цвета найденной точки
в нашей сцене цвет состоит из нескольких компонентов:
цвет поверхности (диффузный)
модель освещения по Фонгу(phong shading)
ambient occlusion
мягкие тени (soft shadows)
этот список можно было бы продолжать всякими экранными эффектами, компонентами других моделей освещения итд
Непосредственно при моделировании никаких сложных примитивов не использовано,
все объекты собраны из
сфер
float sdSphere( vec3 p, float s ){
return length(p)-s;
}
плоскостей
// horizontal
float sdPlane( vec3 p , float down){
return p.y - down;
}
float plane(vec3 p, vec3 n, float offs) {
return dot(p, n) - offs;
}
усеченных конусов
float sdCappedCone( in vec3 p, in float h, in float r1, in float r2 ){
vec2 q = vec2( length(p.xz), p.y );
vec2 k1 = vec2(r2,h);
vec2 k2 = vec2(r2-r1,2.0*h);
vec2 ca = vec2(q.x-min(q.x,(q.y < 0.0)?r1:r2), abs(q.y)-h);
vec2 cb = q - k1 + k2*clamp( dot(k1-q,k2)/dot2(k2), 0.0, 1.0 );
float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0;
return s*sqrt( min(dot2(ca),dot2(cb)) );
}
цилиндра
float sdCylinder( vec3 p, vec2 h ){
vec2 d = abs(vec2(length(p.xz),p.y)) - h;
return min(max(d.x,d.y),0.0) + length(max(d,0.0));
}
и шума(noise), для создания "сугробов":
float hash(vec3 p) {
p = fract( p*0.3183099+.1 );
p *= 17.0;
return fract( p.x*p.y*p.z*(p.x+p.y+p.z) );
}
float noise( in vec3 x ){
vec3 p = floor(x);
vec3 f = fract(x);
f = f*f*(3.0-2.0*f);
return mix(mix(mix( hash(p+vec3(0,0,0)),
hash(p+vec3(1,0,0)),f.x),
mix( hash(p+vec3(0,1,0)),
hash(p+vec3(1,1,0)),f.x),f.y),
mix(mix( hash(p+vec3(0,0,1)),
hash(p+vec3(1,0,1)),f.x),
mix( hash(p+vec3(0,1,1)),
hash(p+vec3(1,1,1)),f.x),f.y),f.z);
}
Так же тут используется отражение и поворот пространства, для создания 6 снеговиков и звезды, операция smoothMin для создания плавных переходов между поверхностями
Upd2: добавил деталей, теперь летит снег и взаимодействует с другим снегом в сцен
при помощи smooth minimum, двигается источник света, самую актуальную версию смотрите тут
PPS: все происходящее тут можно сильно оптимизировать, начиная от шума, который ту
считается каждый раз, его можно либо запечь в текстуру, либо сразу предоставить текстур
шума, затем функция map()... для разных частей освещения или эффектов можно использоват
различные упрощенные версии map() кандидаты на это - построение теней, ambient occlusion, из последнего убрать бы летящий снег, сам raymarching тоже может быть оптимизирован на счет первоначальной трассировки более простых сцен(кубов или сфер в которых вписаны объекты сцены), для уменьшения количества шагов трассировки
PPPS:
Где можно про это почитать:
Сайт Inigo Quilez.
Простенький туториал от Jamie Wong
ну и конечно же гуглёж по терминам
signed distance field(function), SDF
raymarching, raytracing
UPD 31.03.2019: Починил ( правда ценой производительности (: ) артефакты прошло
версии, когда при взгляде сверху появлялись черно-цветные полосы.
Ответ 4
А вот и Ёлочка!
Я плох в векторной графике и канвасе, но что-то смыслю в математике, поэтому ка
смог :) В общем, можно заметить, что есть очень много общего со скриптом снежинок, которы
я кидал ранее. Соль заключается в том, что позиционированием звездочек полностью занимаетс
js при помощи функции y=sin(x)*x, график которой похож на елку. Перевернув эту функцию на 90 градусов можно получить точки расположения звездочек по оси Y. Размер звездочек вариативный, но зависит от смещения сверху: чем ниже, тем больше звезда, как на картинке. Немного визуальных эффектов и вот :) Хорошо себя чувствует при ресайзе, разверните на весь экран, для примера. Не совсем похоже, конечно, но, думаю, имеет место быть.
let params = {
amount: 800,
duration: {
min: 8,
max: 20
},
size: {
min: 20,
max: 45
}
}
const randomBetween = (a, b) => {
return (a + (Math.random() * (b - a)));
}
const randomArr = (arr) => {
return arr[Math.round(Math.random() * (arr.length - 1))];
}
$(document).ready(() => {
for (let i = 0; i < params.amount; i++) {
let star = $("");
let top = randomBetween(0, randomBetween(50, 100));
let randomSize = (randomBetween(params.size.min, params.size.max) * top / 100);
star.css({
"width": randomSize + "px",
"height": randomSize + "px",
"left": "calc(50% - " + (Math.sin(top) * top) + "px)",
"top": top * 3 + "px",
"animation-duration": randomBetween(params.duration.min, params.duration.max) + "s",
"transform-origin": (50 + randomBetween(-15, +15)) + "% " + (50 + randomBetween(-15, 15)) + "%"
});
$("#root").append(star);
}
});
body,
html {
margin: 0;
width: 100%;
height: 300px;
background: green;
}
#root {
position: absolute;
width: 100%;
height: 100%;
filter: drop-shadow(0 0 2px white);
overflow: hidden;
}
.main-star {
position: absolute;
top: 3px;
left: calc(50% - 15px);
z-index: 9;
width: 30px;
height: 30px;
}
.star {
position: absolute;
animation-name: shake;
transform-origin: center center;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
@keyframes shake {
0%,
100% {
transform: rotate(0deg) scale(1);
opacity: 0;
}
10% {
transform: rotate(50deg) scale(.5);
opacity: 0.7;
}
20% {
transform: rotate(184deg) scale(.7);
opacity: 0.8;
}
30% {
transform: rotate(365deg) scale(1);
opacity: 0.7;
}
40% {
transform: rotate(130deg) scale(.6);
opacity: 0.9;
}
50% {
transform: rotate(10deg) scale(0);
opacity: 1;
}
60% {
transform: rotate(46deg) scale(.4);
opacity: 0.8;
}
70% {
transform: rotate(360deg) scale(.1);
opacity: 0.6;
}
80% {
transform: rotate(240deg) scale(.5);
opacity: 0.5;
}
90% {
transform: rotate(200deg) scale(.9);
opacity: 0.2;
}
}
Ответ 5
Начну для затравки.
Конкурс откроется сегодня, - 24.12.2018 г. с хорошим вознаграждением.
Как раз будем подводить итоги накануне Нового года.
Возьмусь, для примера, сделать первый пункт анимации из списка в вопросе.
Так как я буду использовать в этой заготовке базовые команды svg и простые, общеизвестны
приемы, то можно смело их использовать в конкурсных ответах, это не будет считаться копи-пастом.
Лучи от звезды.
Я делаю в SVG, естественно возможно это реализовать и на CSS, Javascript, jQuery
Как это может быть сделано средствами SVG
При радиусе r="100" длина окружности равна 2 * 3,14 * 100 = 628px
Нужно получить 12 лучей + 12 промежутков = 24 равные части. Одна
часть равна 26,16px
Подставляем длину черты и интервала в атрибут
Увеличиваем ширину строки до stroke-width="200"
Так как строка размещается симметрично по обе стороны от осевой линии окружности
и при r="100px" её ширина должна быть 200px для заполнения внутреннего пространства окружности.
Для демонстрации окружность заполнена цветом fill="purple"
Прототип снежинки
Убираем заливку fill="none" и присваиваем stroke="#87CEFA"
В центр лучей добавляем звёздочку
Добавляем градиент лучей звезды и анимацию, к сожалению анимация в
этом варианте работает только в FF Этот кусок кода закомментирован
Анимация вращения лучей звезды
Для вращения объекта по часовой стрелке на один полный оборот необходимо добавить внутри его тегов команду анимации:
Немного сложна семантика тегов объекта при использовании вложенной анимации.
Внести ясность поможет топик: Эти странные теги SVG
Ниже код анимации вращения лучей звезды
Усложняем алгоритм вращения
values="0 200 200;720 200 200;720 200 200;0 200 200;0 200 200"
Поворот по часовой стрелке на 720 ° → пауза → поворот против часовой стрелк
на 720 ° → пауза
Подбирая углы, их порядок следования, а также изменяя время вращения
dur="3.43s", можно получить интересные эффекты
Варианты анимации фразы - С Новым 2019 годом!
Этот вариант не участвует в конкурсе, создан для примера. Участники могут брать эт
технику анимации прорисовки и раскрашивания букв в качестве заготовки и использовать, творчески переработав, в своих конкурсных ответах.
Как сделать анимацию обводки букв - подробно, с картинками рассказывается как это сделать
update Новый вариант
#2 Анимация волной
Подробно теория и примеры, как это сделать здесь
svg {
background-image: url(https://i.stack.imgur.com/ZNCaY.jpg);
background-size:cover;
width:50%
height:50%
}
#3 Двойная анимация,- волны и букв на ней
svg {
background-image: url(https://i.stack.imgur.com/3ENnq.jpg);
background-size:cover;
width:50%
height:50%
}
d3.js 🎄 интерактивная версия
никаких внешних ресурсов не использовано
updates 06.01.2019:
можно перетащить игрушки на елку, чтобы они замигали
теперь падает снег и есть сугробы
добавлены фильтры для придания плавных переходов между фигугами и для придания псевдо объема игрушкам
updates 08.01.2019:
появляется текст
20 19
Ответ 10
Начну делать елку с звезды. Я сделал лучи заднего плана полностью на линейных градиентах без svg. Звезду позаимстовал у SE и добавил эффект свечения. Получилось вот что-то такое:
html, body {
width: 100%;
height: 100%;
margin: 0;
background-color: lightgreen;
}
#star_back {
position: relative;
width: 100%;
height: 100%;
}
#star_back:after, #star_back:before {
content: '';
position: absolute;
background: linear-gradient(90deg, transparent 50%, black 50%, black), linear-gradient(82deg
transparent 50%, lightgreen 50%, lightgreen), linear-gradient(67deg, transparent 50%
green 50%, green), linear-gradient(52deg, transparent 50%, lightgreen 50%, lightgreen)
linear-gradient(37deg, transparent 50%, green 50%, green), linear-gradient(22deg, transparen
50%, lightgreen 50%, lightgreen), linear-gradient(7deg, transparent 50%, green 50%
green), linear-gradient(-8deg, transparent 50%, lightgreen 50%, lightgreen), linear-gradient(-23deg, transparent 50%, green 50%, green), linear-gradient(-38deg, transparent 50%, lightgreen 50%, lightgreen), linear-gradient(-53deg, transparent 50%, green 50%, green), linear-gradient(-68deg, transparent 50%, lightgreen 50%, lightgreen), linear-gradient(-83deg, transparent 50%, green 50%, green), linear-gradient(-90deg, transparent 50%, lightgreen 50%, lightgreen), radial-gradient(transparent 90%, white 10%);
background-position: 0% 0%;
background-size: 200% 100%;
height: 200px;
width: 120px;
border-top-left-radius: 50%;
border-bottom-left-radius: 50%;
}
#star_back:before {
left: 120px;
transform: rotate(180deg);
}
#star_gradient{
background: radial-gradient(transparent, lightgreen);
width: 240px;
height: 220px;
z-index: 1;
position: absolute;
}
#star{
width: 80px;
height: 80px;
margin-left: 80px;
margin-top: 60px;
opacity: 1;
-webkit-filter: drop-shadow( -5px -5px 5px gold );
filter: drop-shadow( 0px 0px 10px gold );
}
Ответ 11
Остальное делать время нет...если будет время до рисую до конца весь мультик ка
я и задумал
.tree {
fill: url(#pattern);
}
.top-star {
fill: red;
stroke-width: 2;
stroke: yellow
}
.star-tree {
opacity: .8;
paint-order: markers stroke fill;
}
.tree-line {
fill: none;
stroke: #e09a11;
stroke-width: 2px;
stroke-linecap: butt;
stroke-linejoin: miter;
stroke-opacity: 1;
stroke-miterlimit: 4;
stroke-dasharray: 0.26499999, 0.52999997;
stroke-dashoffset: 0
}
.fill-gray {
fill: #ccc;
stroke: none;
opacity: 0.5;
}
.green {
fill: #00ff00;
}
.blue {
fill: #0000ff;
}
.pink {
fill: #ff5555;
}
.red {
fill: #ff0000;
}