Как это работает. Под капотом у крутяшек (часть 2, html5)

+114
в блоге Блог им. V747


Продолжаем.
У нас есть 100 картинок из блендера пронумерованных от 0000 до 0099, всего 100 штук.
Результат getturnable.tumblr.com/goodly
СпойлерШаг первый, ищем хостинг с прямыми ссылками.
Отвечу кратко, бесплатных хостингов с прямыми ссылками на данный момент не существует. Единственная нормальная замена это гугл сайты, про которые уже никто не помнит.
Регистрируемся и создаем файловую структуру, вида: название сайта>название модели>папка с фреймами
Процесс создания сайта опущу, потому, что интерфейс там простейший.
Хостинг мы нашли, теперь все наши ссылки будут иметь вид sites.google.com/название сайта/название папки с моделью/папка с фреймами/0000-0099.png

Шаг второй, ищем доверенный сайт для deviantart, в качестве адекватной площадки для html5 подойдет tumblr, да, он живет и здравствует по сей день и имеет возможность создания кастомного интерфейса с правкой кода страницы.

Шаг третий, пишем код-для этого идем в настройки внешнего вида тамблера

Прокатываем левый столбик с кастомизацией до низа и жмем добавить страницу, в открывшемся окне выбираем custom layout и спускаемся в редактор html кода

Код страницы очень прост и состоит из нескольких шагов.
1)Загрузка картинок.
2)Привязка событий мыши/планшета
3)Простые плюшки в виде перехода в фуллскрин

Для начала пропишем стили
#showTurnable {

             display: block;
             
  }
 .waiting
{
      background-color: black;
}

 .loaded
{
     transition: background-color 0.5s ease;
     background-color: #D0D0D0;
}

.show {
    -moz-transition: opacity 0.7s linear;
    -o-transition: opacity 0.7s linear;
    -webkit-transition: opacity 0.7s linear;
    transition: opacity 0.7s linear;
}

.hide {
        -moz-transition: opacity 0.7s linear;
    -o-transition: opacity 0.7s linear;
    -webkit-transition: opacity 0.7s linear;
    transition: opacity 0.7s linear;
    opacity: 0;
} 

html,body{

  overflow-y:hidden;
   overflow-x:hidden;

}

img:-webkit-full-screen {
  width: 100% !important;
}
img:-moz-full-screen {
  width: 100% !important;
}
img:-ms-fullscreen {
  width: 100% !important;
}
img:fullscreen {
  width: 100% !important;
}


button {
   position: absolute;
    left:90%;
    top:0%;
    height:100%;
    padding:4%;
    opacity:0;
     user-select: none;
        -moz-user-select: none;
        -khtml-user-select: none;
        -webkit-user-select: none;
        -o-user-select: none;
}

::selection {
    background: transparent;
}
::-moz-selection {
    background: transparent;
}

tumblr_controls, .tmblr-iframe{display:none !important;}





#loader {
    position:absolute;
    top:0%;
    left:0%;
    right:0%;
    bottom:0%;
    margin:auto;
    width: 40%;

}

img{pointer-events: none;}


showTurnable -канвас,waiting и loaded а также show и hide отвечают за появление прелодаера, и плавное изменение цвета страницы при всех загруженных изображениях.
Далее настраиваем местоположение картинки загрузки, канваса, снимаем событие захвата картинки чтобы она не тянулась за курсором и приделываем кнопку для перехода в фулскрин на экране планшета, она скрыта и располагается вдоль всего правого края экрана.
Тело страницы имеет класс loading при загрузке, по окончанию ее имя класса меняется и страница меняет цвет, точно также реализовано и появление канваса и исчезание картинки загрузки, к каждому из них присвоены свои классы, меняющиеся при загрузке.
Сама процедура загрузки идет циклом, создает объекты картинок, и присваивает им url расположения
var imgObj = new Array();          // массив объектов изображений
         for(i = 0; i < 100; i++){
            imgObj[i]=  document.createElement('img')
          //  imgObj[i] = new Image()   // создаем объект изображения
            if(i<10)
            {
                imgObj[(i)].src = "https://sites.google.com/site/home/images/модель/src/000"+i.toString()+".jpg";
            }
            else
            {
                    imgObj[(i)].src = "https://sites.google.com/site/home/images/модель/src/00"+i.toString()+".jpg";
            }
            
        }


На событие загрузки 99ой картинки вешается функция как раз смены классов, и отрисовки канваса с уже заданным размером. Это плохо, надо цепляться к событию загрузки каждого фрейма и в теле функции увеличивать значение на единицу, и если в финальной функции значение счетчика действительно 100 то запускать процедуры отображения канваса, но за все время существования гугл сайты еще ни разу не падали и не вываливали ошибки при доступе к картинкам.
Функция ресайза картинок для подгона по размеру окна
 function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {

          var ratio =Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
    
           return { width: srcWidth*ratio, height: srcHeight*ratio };
        }

   scaledW=calculateAspectRatioFit(imgObj[0].width,imgObj[0].height,window.innerWidth,window.innerHeight).width;
                scaledH=calculateAspectRatioFit(imgObj[0].width,imgObj[0].height,window.innerWidth,window.innerHeight).height;
                 
                showturn.width=Math.round(scaledW);
                showturn.height=Math.round(scaledH);
                 showturn.src=imgObj[counter].src;

Это в случае с реализацией с канвасом, если использовать просто див и в нем картинку, то можно циклом в момент изменения размера окна/ориентации экрана, присваивать всем картинкам размер через стили.
Простой код для изменения видимости канваса
 function showcanvas() {
  canvas.classList.remove("hide");
    document.body.classList.remove("waiting");
     document.body.classList.add("loaded");
  }

      function hideimg() {
  document.getElementById("loader").classList.remove("show");
    document.getElementById("loader").classList.add("hide");
    
    
  
  }


Создаем две функции которые будут использоваться для отслеживания перемещения пальца/курсора
var ctx = canvas.getContext("2d");
                document.addEventListener('mousemove', handler, false);
              //  document.addEventListener('touchmove', handler2, false);
                 window.addEventListener("touchmove", handler2, {passive: false} );
                function handler(e)  {
            if((e.buttons==1)){
                    e.preventDefault();
                if (e.pageX < oldx) {
            direction = "left";
            counter--;
             } else if (e.pageX > oldx) {
            direction = "right";
             counter++;
             }
               
                 oldx = e.pageX;
                 mousePos.x = e.x;
                 
                  if(counter==100)
        {
            counter=0;
        }
        if(counter==-1)
        {
            counter=99;
        }
   
      ctx.drawImage(imgObj[counter], 0, 0,scaledW,scaledH);
            }
       
        }
        function handler2(e)  {
            e.preventDefault();
                if (e.changedTouches[0].pageX < oldx) {
            direction = "left";
            counter--;
             } else if (e.changedTouches[0].pageX > oldx) {
            direction = "right";
             counter++;
             }
               
                 oldx = e.changedTouches[0].pageX;
                 mousePos.x = e.changedTouches[0].pageX;
            
        if(counter==100)
        {
            counter=0;
        }
        if(counter==-1)
        {
            counter=99;
        }
     ctx.drawImage(imgObj[counter], 0, 0,scaledW,scaledH);
        }
        


Вешаем плюшки в виде фулскрина по дабл клику
    //фулскрин по дабл клику
    var isfullScreen=false;
   document.ondblclick = function(e){
       e.preventDefault();
       if (isfullScreen==false) {
          
          openFullscreen();
             isfullScreen=true;
        }
        else
        {
              closeFullscreen();
               isfullScreen=false;
        }
       
   };

document.getElementById('full').onclick = function(e){
       e.preventDefault();
       if (isfullScreen==false) {
          
          openFullscreen();
             isfullScreen=true;
        }
        else
        {
              closeFullscreen();
               isfullScreen=false;
        }
       
   }; 


var elem = canvas;
function openFullscreen() {
  if (elem.requestFullscreen) {
    elem.requestFullscreen();
  } else if (elem.mozRequestFullScreen) { /* Firefox */
    elem.mozRequestFullScreen();
  } else if (elem.webkitRequestFullscreen) { /* Chrome, Safari & Opera */
    elem.webkitRequestFullscreen();
  } else if (elem.msRequestFullscreen) { /* IE/Edge */
    elem.msRequestFullscreen();
  }
}

function closeFullscreen() {
  if (document.exitFullscreen) {
    document.exitFullscreen();
  } else if (document.mozCancelFullScreen) {
    document.mozCancelFullScreen();
  } else if (document.webkitExitFullscreen) {
    document.webkitExitFullscreen();
  } else if (document.msExitFullscreen) {
    document.msExitFullscreen();
  }
}

Да, код отвратителен, но приводится просто как пример, для общего описания процесса. Необходимо еще добавить
<meta name="viewport" content="width=device-width, initial-scale=1">

для исключения масштабироания на мобильных девайсах при двойном тычке


Теги:

  • В избранное
    7

46 комментариев

Издалека показалось что это рисунок)
Часто 2.8 крашится? На Eevee.
Сейчас уже нет, стал довольно стабильным
Ня
Нужна (естественная) популяризация крутяшек. Быть может, тогда в IT появятся нормальные форматы и программы для просмотра (если будет ещё разделение каналов для 3D, то будет ещё лучше)
Спойлересли нужно привести число к фиксированной длине
imgObj[(i)].src = "https://sites.google.com/site/home/images/модель/src/000"+i.toString()+".jpg";

можно воспользоваться двумя методами
1) Number.toFixed(decimals)- преобразует число в строку, оставив decimals знаков после запятой
2) String.padStart(length, filler) — дотянет длину строки до length, добавляя к началу filler
то есть, можно переписать код так:
imgObj[(i)].src = "https://sites.google.com/site/home/images/модель/src/" + i.toFixed(0).padStart(4, '0') + ".jpg";


scaledW=calculateAspectRatioFit(imgObj[0].width,imgObj[0].height,window.innerWidth,window.innerHeight).width;
scaledH=calculateAspectRatioFit(imgObj[0].width,imgObj[0].height,window.innerWidth,window.innerHeight).height;

вызывать два раза одну и ту же функцию, чтобы получить разные поля возвращаемого объекта — не комильфо

во-первых, можно присвоить результат переменной и она станет объектом с нужными полями
scaled = calculateAspectRatioFit(imgObj[0].width,imgObj[0].height,window.innerWidth,window.innerHeight);
showturn.width=scaled.width;
showturn.height=scaled.height;

чуть более модерновый вариант — использовать деконструирующее присваивание, например так
{width, height} = calculateAspectRatioFit(imgObj[0].width,imgObj[0].height,window.innerWidth,window.innerHeight);


Эти округления не нужны
showturn.width=Math.round(scaledW);
showturn.height=Math.round(scaledH);


А ещё можно отображать картинку, задавая её фоном для дива
что нужно прописать в стилях
background-position: center center;
background-repeat: no-repeat;
/* собственно вот: contain - подогнать размер картинки так, чтоб целиком вошла в контейнер, cover - чтобы целиком покрыла контейнер */
background-size: contain;


ну и, собственно, вписывать урл как-то так
containerDiv.style.backgroundImage = `url('${imageURL}')`
бесплатных хостингов с прямыми ссылками на данный момент не существует
Imgur же?
Хотя если очень хочется сохранить 0000-0099 в названиях, то не прокатит, да
Я чёт слегка взбесился от совершенно дубовой скорости вращения мышкой и случайно переписал всё на свой лад andreymal.org/files/turnable_test/
На тумбе не проверял, в старых браузерах не проверял, зато обработка скорости нормальная и число картинок не обязательно ровно сто
Воу… Я… 0_о как?..
Это охуенно
как?

Если это действительно вопрос и он про скорость, то оно сводится к вычислению угла поворота, который задаётся мышкой, а уже из угла поворота вычисляется номер картинки, как-то так:
Спойлер
var oldAngle = чотототам;
var imagesCount = 100;
var speed = 1.0;

// ...

var deltaX = e.pageX - oldx;
var angle = oldAngle + deltaX * speed;

if (angle >= 360 || angle < 0) {
  // Запихивание угла в диапазон 0..359
  angle -= Math.floor(angle / 360) * 360;
}

// Конвертирование угла (0..359) в номер картинки (0..99)
var idx = Math.round(angle * imagesCount / 360.0);
if (idx >= imagesCount) {
  idx -= imagesCount;
}

oldAngle = angle;
counter = idx;

Но просто поправить несколько строчек в коде неинтересно, я люблю переписывать всё подряд)
А остальное наверно и так можно в исходниках прочитать
Я охреневший сижу честно)
Проблема с торможением канваса не решалась я хз сколько, спасибо тебе
Я бы увеличил «штраф» движения за величину движения мыши по вертикали, раза в два. Ну а в идеале для каждого из 100 скриншотов нужно сделать еще 100 с поворотом по третей оси, чтобы было полноценное 3D
Настоящее 3D вместо картиночного в таком случае окажется намного более производительным)
Почему? Неужели в современной видеопамяти не поместятся 100Х100 фулл-хд картинок?
Ну сам посчитай: 1920*1080*3*100*100 = 58 гигов
А тройка откуда?
1 байт красный, 1 байт зелёный, 1 байт синий (и скорее всего ещё 1 байт на альфа-канал и/или выравнивание, но я решил сжалиться)
Щас бы хранить изображения без алгоритмов сжатия, лул.
Щас бы обрабатывать сжатые изображения на 60фпс на тупой видеокарте с примитивными графическими ядрами, ага
А можно мне 144 фпс, или нахуя я тратил 15к на 144гц моник?
Можно конечно:
Настоящее 3D вместо картиночного в таком случае окажется намного более производительным)
Кстати, в предыдущем посте в исходном коде я не увидел ни намека на ограничение в 60фпс. То есть либо это вшито в ограничения браузеров, либо все-таки у меня 144фпс, ибо на глаз двигается всё плавно (для меня всё что 60фпс — это слайд-шоу, потому что я уже привык к 144)
Но ладно, возьмём даже какое-нибудь сжатие с потерями типа S3TC, которое поддерживается видеокартами, делим размер на 6 — всё равно 10 гигов, в мой ноутбук не влезает)
Ну у моей 1050ti 3 гига. Если не full hd а просто hd, то почти тютелька в тютельку, если отключить всю остальную графику ОСа и браузера
+1 за полноценное 3D, хотя и не в виде 100x100 картинок.
Ну, половина из этого набора будет реверсом других картинок. Можно срезать вдвое. Ну и можно так-же срезать всё то, что снизу. Это еще треть примерно. Остается 100Х35 примерно
Не думаю, что для полноценного 3D уместны какие-то реверсы.
Что снизу срезать?
И там всё равно нужно ещё и передавать много данных. Я умножил размер одной из картинок на 100x100 — получилось 990210000 байт. Даже если сократить 100 до 35, будет многовато. Можно помещать сразу по многу картинок в один JPG/PNG, но не факт, что и это сильно поможет.
Кстати да, можно сделать одну сжатую огромную картинку, в виде сиквенции всех кадров. И чтобы сайт показывал только определеные вырезки из этого огромного изображения. Думаю, так будет выгоднее. По такому принципу работает 2D анимация в Unity.
Ага, и какого размера у тебя будет эта мега-текстура? 0.1 гигапикселя (10^8 пкс для 100х100 кадров размером 100х100)?
Нууу и че? Разве для 2к19 это много? Понимаю если бы мы в девяностых жили, но сейчас то…
Браузер имеет кэш 30 мб, вывалится, я уже пробовал скеливать длинные картинки
Можно ли использовать в будущем с указанием ссылки на тебя?
Ага, если уровень поддержки браузерами устраивает)
Ой, я там немного увлёкся с размерами канваса и случайно сделал мыльцо на айфончиках и на не-100% масштабе на компах
Баг починил, devicePixelRatio учёл, тестовая страница andreymal.org/files/turnable_test/chess.html
Тестовая пикча должна не мылиться и абсолютно точно попадать в пиксели экрана в любых ситуациях
Тестовая пикча должна не мылиться и абсолютно точно попадать в пиксели экрана в любых ситуациях

Да, но во время скроллинга она «шумит»
Юзай старые добрые ЭЛТ-мониторы, там «шуметь» не будет
Я попробовал на 60гц и 144гц разных мониках, и на айпаде. Шум везде одинаковый. Что такое ЭЛТ я в душе не клопаю.
Ну и ньюфажина)
Ой, а я там немного увлёкся с размерами канваса и случайно дёргаю DOM. Убрал дёргание DOM, попутно заюзал createImageBitmap — в хроме на компе стало на 5% быстрее, а на моём телефоне так вообще во все два раза шустрее
Запилил простенький бенчмарк https://andreymal.org/files/turnable_test/bench.html
Без ImageBitmap

С ImageBitmap (без аппаратного ускорения)

С ImageBitmap и глюковатым аппаратным ускорением
Хром при этом жрёт около 300 метров видеопамяти, кстати
Затестил Bench у себя на
ПК:привязка fps к частоте монитора)Опера 58.0.3135.68
Огнелис съел еще больше
Огнелис 65.0.1
Как-то слишком много. Попробуем обновить :D
Огнелис 66.0
Другое дело! Теперь что Хром, что Лис, кушают ± одинаково


Что касается портативных устройств:
Adreno 530:Битмап не влиял на фпс, я так понял, ограничение 60фпс.
Опера, Хром, Эдж — 55-60фпс
Огнелис — 15-25фпс. 65.0.1 :D Самая новая в Google play
Ушел на 4pda, скачивать 66 версию.
Огнелис 66 — Увы, 22-28фпс.

Mali-400 MP4:Хром 54-60фпс. Периодически замирает.
Открыл chess.html, такие же фризы, как у тебя на видео. Получил более плавную картинку, заморозив Service GP и сам GP, отжирают много озу, и так всего 2гб
Кстати, сегодня я случайно узнал, что видеопроцессор Mali-400 — говно. Вместо картинок рисует какой-то хлам, а если и отрисует картинки, то непонятно в каком порядке
Спойлер
Гугл говорит, что такое в любом хроме на любых устройствах с Mali-400, и лечится это только отключением аппаратного ускорения канвасов в настройках хрома (ну или оперы в моём случае), такие дела
А я скачал «скриптом» все картинки и теперь могу «крутить» в локальном просмотрщике картинок IrfanView. Хоть и не мышкой и без контроля скорости, но всё равно неплохо
Скачай Blender, выпроси blend-файл и крути в тридэ ))
Вообще было бы неплохо
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.