Как это работает. Под капотом у крутяшек (часть 1, flash)
+156
Несколько людей спросили, как вообще собираются картинки с понями, которые можно крутить, попытаюсь сжато рассказать.
Разбирать будем на реальном примере
Сразу говорю, код который приведен ниже отвратительный и приводится исключительно для объяснения, как это работает.
Итак, у нас есть задача, сделать Луну в подарок одному человеку и где то 4 дня на моделирование, сборку и размещение крутяшек. Поехали.

Идея тюрнейблов не новая от слова совсем. На сайтах автомобилей, ювелирных украшений, бытовой техники есть превью картинки с несколькими фреймами, которые можно вращать. Некоторые сайты используют полноценные 3д вьюверы для своей продукции. Это и хорошо и плохо. У каждого типа есть свои ограничения и преимущества.
СпойлерЕсли говорить простым языком это управляемая секвенция-набор кадров, снимающие объект в моем случае с шагом в 3.6 градуса.
3.6 градуса, 100 картинок и скрипт который управляет их сменой и вешает некоторые плюшки, вот и все, спасибо.
Шучу. При таких входных данных надо решать следущие проблемы.
1) Вес.
Кэш современных браузеров ограничен, где то 30мб, и надо в этот вес уложится. С постепенным переходом девиантарта на html5 надо учитывать, что мобильные устройства имеют свои особенности в распределении памяти. Иными словами-нам нужен контейнер.
2)Хранение.
Если в случае с флеш крутяшкой контейнером выступает сам ролик swf, то в случае html, нужно искать сервер, который поддерживает прямые пути и папочную иерархию с прямыми именами файлов.
3)Отклик (до сих пор полностью не решено)
100 картинок должны одинаково быстро меняться при движении курсора и свайпа пальцем, как на экране монитора, так и на мобильных девайсах. Во втором случае обеспечить быстродействие сложнее, устройства все разные и если где то будет летать на планшете, то не факт, что будет хотя бы 3 фпс, на каком нибудь старом телефоне.
4)Ссылки. Неочивидный момент, но для html5 крутяшек недостаточно поднять свой сервер. Девиантарт не дает вставлять ссылки на непроверенные сайты. Значит нужно воспользоваться тем сайтом который входит в списки доверенных на DA
Эта часть посвящена только флеш ролику, иначе объем текста получится громадным, в html 5 сделать тоже самое сложнее.
Изначально секвенция собирается в блендере, там же редактируется и отправляется во флеш.
В блендре ставится фигурка, вокруг нее кривая-кольцо сплайн, на него вешаем камеру с типом следования follow path, в центр кольца добавляем пустышку и к ней трачим камеру.
Тип трака:



После настройки сцены идем на sheepit-renderfarm.com/ и загружаем проект.
Идем пить чай.
После пары (десятков) часов получаем готовую секвенцию.
Идем во флеш.
Решения для флеш крайне просты.
Я использую Adobe Flash Professional CS 5.5
Создаем новый проект, в свойствах сцены выставляем размеры и цвет фона как у сайта Devianart

Созадем два ключевых кадра-на одном прелоадер, на втором наша сцена.

Переходим на первый кадр и пишем такой код
Все, прелоадер готов.
Идем на второй фрейм и создаем новый символ

Во всплывшем окне нажимаем OK, теперь мы сидим в символе (символ во флеше-это картинка или секвенция, которая имеет свои собственные слои, линию воспроизведения и управление по обращению к имени инстанса).Идем в файл-> импортировать секвенцию в сцену

Секвенция должна иметь вид: 0001.png,0002.png (именно png-это позволит впоследствии настроить качество финальной флешки при сохранении,jpg не дает такого сделать)и так далее, двойной клик на первом фрейме, программа предлагает загрузить все файлы.
Идем попить чаю

После того как секвенция загрузилась, выходим из символа и видим в билиотеке все наши фреймы и наш символ

Захватываем его мышкой и тащим на сцену, прасполагаем так чтобы был подогнан по всем углам. Дабл клик на символе, мы внутри него, идем на таймлайн и выбрав все фреймы символа нажимаем пкм и выбираем reverse frames, это нужно для того, чтобы крутяшка не вращалась в противодействие курсору, выходим из символа кликнув на слово сцена

Идем на вкладку actions, при выбранном втором кадре и пишем следующий код
Выделяем символ и нарекаем его именем maincontent

В коде мы останавливаем воспроизведение символа, делаем альфу в ноль, вешаем событие fade in, подцепляемся к мышке и просто закольцовываем наши фреймы, при достижении 100 го фрейма он сменяется на 1ый.
Далее идут функции управления с клавиатуры и сохранения изображения.
Теперь необходимо настроить качество выходной флешки


Нам нужно уложиться в ограничение на 30 мб от дивана, ставим поочередно от 98 процентов и ниже, сохраняемся и смотрим сколько весит ролик. При достижении нижней границы порога, сохраняем, пьем чай и грузим на да флеш ролик.
Получаем такое
В данном случае проблемы описанные в начале
1)Контейнер-сам ролик флеш
2)Сайт DA предоставляет возможность грузить флеш ролики напрямую
3)Отклик, в данном случае код написанный на ac3 не особо шустрый, можно цепляться непосредственно за событие перемещения курсора, а не событие смены кадра
4)Проблемы со ссылками будут решаться в случае с html 5 роликом, здесь все собрано в самом проекте.
Позже, если будет интересно, допишу про html 5 сборку.
Разбирать будем на реальном примере
Сразу говорю, код который приведен ниже отвратительный и приводится исключительно для объяснения, как это работает.
Итак, у нас есть задача, сделать Луну в подарок одному человеку и где то 4 дня на моделирование, сборку и размещение крутяшек. Поехали.

Идея тюрнейблов не новая от слова совсем. На сайтах автомобилей, ювелирных украшений, бытовой техники есть превью картинки с несколькими фреймами, которые можно вращать. Некоторые сайты используют полноценные 3д вьюверы для своей продукции. Это и хорошо и плохо. У каждого типа есть свои ограничения и преимущества.
СпойлерЕсли говорить простым языком это управляемая секвенция-набор кадров, снимающие объект в моем случае с шагом в 3.6 градуса.
3.6 градуса, 100 картинок и скрипт который управляет их сменой и вешает некоторые плюшки, вот и все, спасибо.
Шучу. При таких входных данных надо решать следущие проблемы.
1) Вес.
Кэш современных браузеров ограничен, где то 30мб, и надо в этот вес уложится. С постепенным переходом девиантарта на html5 надо учитывать, что мобильные устройства имеют свои особенности в распределении памяти. Иными словами-нам нужен контейнер.
2)Хранение.
Если в случае с флеш крутяшкой контейнером выступает сам ролик swf, то в случае html, нужно искать сервер, который поддерживает прямые пути и папочную иерархию с прямыми именами файлов.
3)Отклик (до сих пор полностью не решено)
100 картинок должны одинаково быстро меняться при движении курсора и свайпа пальцем, как на экране монитора, так и на мобильных девайсах. Во втором случае обеспечить быстродействие сложнее, устройства все разные и если где то будет летать на планшете, то не факт, что будет хотя бы 3 фпс, на каком нибудь старом телефоне.
4)Ссылки. Неочивидный момент, но для html5 крутяшек недостаточно поднять свой сервер. Девиантарт не дает вставлять ссылки на непроверенные сайты. Значит нужно воспользоваться тем сайтом который входит в списки доверенных на DA
Эта часть посвящена только флеш ролику, иначе объем текста получится громадным, в html 5 сделать тоже самое сложнее.
Изначально секвенция собирается в блендере, там же редактируется и отправляется во флеш.
В блендре ставится фигурка, вокруг нее кривая-кольцо сплайн, на него вешаем камеру с типом следования follow path, в центр кольца добавляем пустышку и к ней трачим камеру.
Тип трака:



После настройки сцены идем на sheepit-renderfarm.com/ и загружаем проект.
Идем пить чай.
После пары (десятков) часов получаем готовую секвенцию.
Идем во флеш.
Решения для флеш крайне просты.
Я использую Adobe Flash Professional CS 5.5
Создаем новый проект, в свойствах сцены выставляем размеры и цвет фона как у сайта Devianart

Созадем два ключевых кадра-на одном прелоадер, на втором наша сцена.

Переходим на первый кадр и пишем такой код
/*
устанавливаем значение переменных
*/
var w:uint = 200; // ширина прелоадера
var h:uint = 10; // высота прелоадера
var colorFon:uint = 0xcccccc; // цвет фона прелоадера
var color:uint = 0x333333; // цвет индикатора загрузки
/*
останавливаем воспроизведение флеш-ролика в первом кадре
*/
stop();
/*
рисуем контейнер прелоадера
*/
var preloaderContainer:Sprite = new Sprite();
preloaderContainer.graphics.lineStyle(1,color,1);
preloaderContainer.graphics.beginFill(colorFon,1);
preloaderContainer.graphics.drawRect(0,0,w,h);
preloaderContainer.graphics.endFill();
addChild(preloaderContainer);
/*
помещаем наш прелоадер на середину флеш-ролика
*/
preloaderContainer.x = (stage.stageWidth - w) / 2;
preloaderContainer.y = (stage.stageHeight - h) / 2;
/*
рисуем индикатор загрузки
*/
var band:Sprite = new Sprite();
band.graphics.beginFill(color,1);
band.graphics.drawRect(0,0,w,h);
band.graphics.endFill();
preloaderContainer.addChild(band);
/*
в каждом новом кадре проверяем объём загруженной информации и на основе этой информации изменяем индикатор загрузки
*/
addEventListener(Event.ENTER_FRAME, onEnterFrames);
function onEnterFrames(event:Event)
{
var bLoaded:uint = loaderInfo.bytesLoaded;
var bTotal:uint = loaderInfo.bytesTotal;
band.scaleX = bLoaded / bTotal;
/*
если флеш-ролик загружен полностью, то удаляем сам прелоадер и переходим на следующий кадр
*/
if (bLoaded >= bTotal)
{
removeChild(preloaderContainer);
removeEventListener(Event.ENTER_FRAME, onEnterFrames);
nextFrame(); // или gotoAndPlay(номер_кадра);
}
}Все, прелоадер готов.
Идем на второй фрейм и создаем новый символ

Во всплывшем окне нажимаем OK, теперь мы сидим в символе (символ во флеше-это картинка или секвенция, которая имеет свои собственные слои, линию воспроизведения и управление по обращению к имени инстанса).Идем в файл-> импортировать секвенцию в сцену

Секвенция должна иметь вид: 0001.png,0002.png (именно png-это позволит впоследствии настроить качество финальной флешки при сохранении,jpg не дает такого сделать)и так далее, двойной клик на первом фрейме, программа предлагает загрузить все файлы.
Идем попить чаю

После того как секвенция загрузилась, выходим из символа и видим в билиотеке все наши фреймы и наш символ

Захватываем его мышкой и тащим на сцену, прасполагаем так чтобы был подогнан по всем углам. Дабл клик на символе, мы внутри него, идем на таймлайн и выбрав все фреймы символа нажимаем пкм и выбираем reverse frames, это нужно для того, чтобы крутяшка не вращалась в противодействие курсору, выходим из символа кликнув на слово сцена

Идем на вкладку actions, при выбранном втором кадре и пишем следующий код
import com.adobe.images.JPGEncoder;
import flash.net.FileReference;
import flash.net.FileFilter;
maincontent.stop();
maincontent.gotoAndStop(1);
maincontent.alpha=0;
stage.addEventListener(MouseEvent.MOUSE_DOWN, mDown);
stage.addEventListener(MouseEvent.MOUSE_UP, mUp);
maincontent.addEventListener(Event.ENTER_FRAME, fadein);
function fadein(e:Event){
if(maincontent.alpha <1){
maincontent.alpha +=0.05;
}
}
var prevMouseX:int;
var playingStatus:int = 0;
function mDown(event:MouseEvent):void {
prevMouseX = stage.mouseX;
maincontent.addEventListener(Event.ENTER_FRAME,mMove);
}
function mUp(event:MouseEvent):void {
maincontent.removeEventListener(Event.ENTER_FRAME,mMove);
}
function mMove(event:Event):void {
var activeMouseX = stage.mouseX;
if (activeMouseX > prevMouseX){
if(maincontent.currentFrame == 1){
maincontent.gotoAndStop(maincontent.totalFrames);
} else {
maincontent.prevFrame();
}
}
if (activeMouseX < prevMouseX){
if(maincontent.currentFrame == maincontent.totalFrames) {
maincontent.gotoAndStop(1);
} else {
maincontent.nextFrame();
}
}
prevMouseX = stage.mouseX;
}
//----управление с клавиатуры
stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyPressed);
function KeyPressed(event:KeyboardEvent)
{
if(event.keyCode==39)
{
if(maincontent.currentFrame == 1){
maincontent.gotoAndStop(maincontent.totalFrames);
} else {
maincontent.prevFrame();
}
}
if(event.keyCode==37)
{
if(maincontent.currentFrame == maincontent.totalFrames) {
maincontent.gotoAndStop(1);
} else {
maincontent.nextFrame();
}
}
if(event.keyCode==83)
{ maincontent.stop();
playingStatus=0;
var bitmapData:BitmapData=new BitmapData(maincontent.width, maincontent.height);
bitmapData.draw(maincontent);
//
var jpgEncoder:JPGEncoder = new JPGEncoder(100);
var byteArray:ByteArray = jpgEncoder.encode(bitmapData);
var imagesFilter:FileFilter = new FileFilter("Images", "*.jpg;*.gif;*.png");
var fileReference:FileReference=new FileReference();
fileReference.save(byteArray,'frame №'+maincontent.currentFrame.toString()+'.jpg');
}
if(event.keyCode==32)
{ playingStatus++;
if(playingStatus==2)
{
playingStatus=0;
}
switch (playingStatus){
case 1:
maincontent.play();
break;
case 0:
maincontent.stop();
break;
}
}
}
Выделяем символ и нарекаем его именем maincontent

В коде мы останавливаем воспроизведение символа, делаем альфу в ноль, вешаем событие fade in, подцепляемся к мышке и просто закольцовываем наши фреймы, при достижении 100 го фрейма он сменяется на 1ый.
Далее идут функции управления с клавиатуры и сохранения изображения.
Теперь необходимо настроить качество выходной флешки


Нам нужно уложиться в ограничение на 30 мб от дивана, ставим поочередно от 98 процентов и ниже, сохраняемся и смотрим сколько весит ролик. При достижении нижней границы порога, сохраняем, пьем чай и грузим на да флеш ролик.
Получаем такое
В данном случае проблемы описанные в начале
1)Контейнер-сам ролик флеш
2)Сайт DA предоставляет возможность грузить флеш ролики напрямую
3)Отклик, в данном случае код написанный на ac3 не особо шустрый, можно цепляться непосредственно за событие перемещения курсора, а не событие смены кадра
4)Проблемы со ссылками будут решаться в случае с html 5 роликом, здесь все собрано в самом проекте.
Позже, если будет интересно, допишу про html 5 сборку.
34 комментария
Ах как давно я не видел этой фразы)
Крутой пост, спасибо!
Ты, наверно, уже отвечал, но какими мощностями ты располагаешь, или сейчас всё у «овечки» рендерится? Сотня картинок, это не мало. Плюс, у тебя всякие эффекты, типо шершавости были. Вопрос может глупый, но я до рендера сам не доходил.
Наивно полагал, что индикатор загрузки, это стандартная флешная штука.
Ну и Луна класс, да.
Комп подарен Bra1n_Eater ом
Рендерю сейчас почти все на овце, там помогает один парень, за что ему низкий душевный поклон.
Если бы не эти двое ребят, то мне жилось очень туго
Теперь одной тайной меньше)))
Забрал в бибилотеку.
Более того, раз уж мы изголяемся, то, при непрозрачном фоне, можно вообразить себя видеокодеком и каждый кадр после первого сделать difference-кадром. Не знаю, какой от этого получится выигрыш и не подохнет ли браузер, силясь это отрендерить, зато будет проще переключать кадры.
примечаниятам код нужно будет красиво в класс упаковать, опции в объект скинуть и отдавать в конструктор, ну и что-то решить с позиционированием контейнера и сайзингом изображения
есть ещё подозрение, что можно это сделать на чистом css, но с ходу не соображу
анимация — уж какая была
работает на мобилке и десктопе
1 с канвасом-там просто отрисовываются картинки по очереди
2 Див при движении мышки удаляем первого потомка дива-элемент img, и делаем append child, но это перестраивает dom
Буду ковырять=)
Надо еще попробовать свойство visibility, через чистый css можно создав два класса
.show и .hide которые просто будут переключать свойство у img, а в js коде менять названия классов
— Можно переключать классы, это удобнее в смысле отделения стиля от кода, но опять же ведёт к пересчёту конечного стиля элемента
— Канва — хороший вариант, но я с ходу не помню, что у неё с отрисовкой. В том смысле, можно ли ей просто сказать, «эй, канва, нарисуй-ка мне вот этот имадж». Вроде бы можно.
— Мне нравится мой вариант (кто бы сомневался =), потому что у него не пертряхивается дом, все элементы уже там, где они будут при выводе, всё позиционирование отдано на откуп элементу-контейнеру. У меня серьёзные подозрения, что на больших суммарных объёмах графики это будет жрать память как не в себя, и это будет огромный минус, но это нужно тестить. С другой стороны, вряд ли это будет тяжелее ютуба, и в любом случае, не загрузив все кадры плавности анимации не добьёшься, а загрузив их всё равно придётся все держать в памяти.
Попробовал по быстрому поднять у себя на тумбе, увы вариант с опасити ест аж 900 метров на вкладку, css значит трогать дорого =\
Там 100 картинок и они при каждом движении мыши идут циклом переключая опасити у всех 100
Да, канвас очень проста, чисуем через 2д контекст
Вариант с заменой doomа
function setFrame(n) { let lim = n while (lim < 0) { lim += ANIM_LEN } lim = (lim % ANIM_LEN) << 0 for (let i = 0; i < FRAME_REF.length; i++) { FRAME_REF[i].style.display = (i === lim) ? 'initial' : 'none' } }плюс в ините убрать строку FRAME_REF[i].style.opacity = 0
Вот что бывает, когда тестируешь на данных далёких от реальных =)
Либо юзать html5 тэг video, прокручивая его скриптом =/
Вариант выше попробую чуть попоже, сейчас с планша)
сейчас работает на канвасе так getturnable.tumblr.com/cyclone
на перестройке дума так getturnable.tumblr.com/testFull
Я пробовал через свойство playbackRate
Можно сделать через ключевые кадры, но опять же на перескок будет тратиться время