Разработка электроники,

Систем автоматики,

Программного обеспечения

ООО "Антех ПСБ", Санкт-Петербург

+79811865082

anteh@bk.ru
Телеграм: собака antehru

ООО Антех ПСБ примеры

Размещение 3D модели на html странице. При перезагрузке страницы сохраняются: zoom фактор, угловая ориентация rotate, координаты положения panning. Загрузка jsc3d скриптов только, при клике по canvas

18.01.2016 https://anteh.ru

Статья о том, как на своём сайте демонстрировать приятно смотрящиеся 3D модели и объекты.

В ближайшее время планируется дополнить статью последними изменеиями. Добавлена "резиновость" -задание размеров и положения кнопок в процентах. На текущей странице демонстрируются все возможности. Осталось вытягивания canvas в IE 9, в других не проверял

Используются свободные jsc3d библиотеки. Рендеринг алюминиевого корпуса

Проработан ворос размещения 3D моделей как в шаблоне сайта, так и на страницах, но в примере описано размещение на станицах, реализацию размещения 3D canvas и на шаблоне и на странице можно посмотреть через отладчик на этой странице. Решение кроссбраузерное, по крайней мере для не совсем старых версий браузеров.

Клацать мышью по картинке, запустится 3D модель, справа 3D модели CNC станка и крейта, слева текстовые надписи с различными элементами

Canvas или группу canvas.ов размещаем в div с position=relative нужно для правильного позиционирования кнопок-canvas.ов, float=left. Загрузка .js переделана на через head.js загрузчик. Кнопки из canvas -был эксперимент. Кнопки желательно делать в 2D области canvas -с точки зрения скорости загрузки страницы и потребления ресурсов на клиентской стороне. Для отображения 3D модели кликаем по изображению:

scroll -масштаб, мышь -вращение, CTRL+мышь -положение

canvas изображения "кликабельны" Описание

При загрузке страницы отображается фоновое изображение, 3D модель будет отображена после первого клика по canvas. Подгрузка jsc3d скриптов будет произведена также только после клика. Если параметр display3D: 'on' то отображение 3D модели будет произведено сразу же после загрузки страницы. Сохранение состояния отображения 3D модели, пока работает хоть один экземпляр браузера, работает для всех canvas.ов. Установлено ограничение на максимальный и минимальный масштаб 3D модели общее свойство для всех canvas. При перезагрузке или смене страницы состояние отображения 3D модели, настроенное пользователем сохраняется -общее свойство. Клиентом меняются масштаб, углы поворота, x y положение модели в canvas.

После загрузки страницы отображается 3D модель и 2 кнопки. 1я сброс ориентации положения масштаба модели в дефолтные. 2я переключение в режим отображения фоновой картинки -если 3D модель вращается, то это может отнимать много процессорных ресурсов, эта функция заменяет 3D на фоновое изображение и полностью освобождает память от 3D данных. Кнопки можно отображать независимо по одной. Кнопки представляют из себя canvas, которые также настраиваются и могут отображать 3D модели, в том числе и вращающиеся со всеми необходимыми ограничениями и параметрами как у основного canvas.а

3D модель и 2 кнопки, кнопка сброса вращается. 3D изображение каждой кнопки можно выбирать.

3D модель и 2 кнопки вращаются. Можно задать вращение по одной или нескольким осям.

Отображается 3D модель с 2мя кнопками. Все 3 canvas элемента, основной и 2 кнопки, находятся в режиме отладки. По клику на одном из 3х canvas над ним появляется отображение текстовой информации с текущими углами ориентации модели в пространстве, x y положением и zoom. Эти данные используются для задания ориентации 3D модели по умолчанию при загрузке. Т.е. запускаете страницу с 3D моделью в режиме отладки, ориентируете её как нужно и полученные значения углов масштаба и положения прописываете в строке конфигурации как самой модели, так и кнопок. Параметры rx, ry, rz -угловая ориентация модели по осям вращения, panx pany и zoomscale

scroll -масштаб, мышь -вращение, CTRL+мышь -положение

scroll -масштаб, мышь -вращение, CTRL+мышь -положение

Детально параметры настройки описаны ниже.

Пропорции ширины высоты canvas должны соотноситься с пропорциями 3D модели. Использую соотношение 16:9: Высота canvas = ширина*9/16

3D модель, создаётся, например во FreeCAD и экспортируется в .obj формате, но лучше в .stl не придётся масштаб исправлять.

Скачиваем jsc3d скрипты с github: jsc3d.min.js jsc3d.touch.js jsc3d.webgl.js(для быстрого примера не обязательно), jquery-1.12.0.js(для быстрого примера не обязательно) если ещё не используется.

Размещаем, например в head:

<script type="text/javascript" src="../js/jsc3d.min.js" async defer></script>
<script type="text/javascript" src="../js/jsc3d.touch.js" async defer></script>

В нужном участке html размещаем блоки, содержащие отображаемые 3D модели, в примере отображается 2 3D модели:

<canvas class="lcolcnv" id="krate"></canvas>
<canvas class="lcolcnv" id="cnc6090"></canvas>

В конце body размещакем скрипт отображения 2х моделей:

<script type="text/javascript">
var viewer = new JSC3D.Viewer(document.getElementById('krate'));
viewer.setParameter('SceneUrl', 'krate.obj');
viewer.setParameter('ModelColor', '#CAA618');
viewer.setParameter('BackgroundColor1', '#E5D7BA');
viewer.setParameter('BackgroundColor2', '#383840');
viewer.setParameter('RenderMode', 'flat');
var viewer2 = new JSC3D.Viewer(document.getElementById('cnc6090'));
viewer2.setParameter('SceneUrl', 'cnc6090.obj');
viewer2.setParameter('ModelColor', '#CAA618');
viewer2.setParameter('BackgroundColor1', '#E5D7BA');
viewer2.setParameter('BackgroundColor2', '#383840');
viewer2.setParameter('RenderMode', 'flat');
viewer.init();
viewer.update();
viewer2.init();
viewer2.update();
</script><script type="text/javascript">
var viewer = new JSC3D.Viewer(document.getElementById('krate'));
viewer.setParameter('SceneUrl', 'krate.obj');
viewer.setParameter('ModelColor', '#CAA618');
viewer.setParameter('BackgroundColor1', '#E5D7BA');
viewer.setParameter('BackgroundColor2', '#383840');
viewer.setParameter('RenderMode', 'flat');
var viewer2 = new JSC3D.Viewer(document.getElementById('cnc6090'));
viewer2.setParameter('SceneUrl', 'cnc6090.obj');
viewer2.setParameter('ModelColor', '#CAA618');
viewer2.setParameter('BackgroundColor1', '#E5D7BA');
viewer2.setParameter('BackgroundColor2', '#383840');
viewer2.setParameter('RenderMode', 'flat');
viewer.init();
viewer.update();
viewer2.init();
viewer2.update();
</script>

Это простейший пример позволяющий быстро увидеть результат. Дальше разбираемся в деталях, например, как задать начальное положение 3D моделей, как защитить файлы моделей.

Детали:

Файлы, .obj .stl или другие, будут передоваться в клиентский браузер и доступны любому, кто загрузил страницу. Нужна минимальная защита. В рамках заметки защита не реализованна, только предположения как сделать. Главная защита -передача файла модели с неполной и искажённой информацией для реверса, не сильно влияющей на внешний вид 3D модели. Выбросить лишнее поможет Blender. Ко всему можно использовать обфускацию с последующим архивированием и паролем, и соответственно, на клиентской стороне, в момент использования 3D модели, обратные преобразования.

Планируется вставить несколько 3D моделей в базовый шаблон сайта, для отображения на всех страницах. "Весить" файлы моделей могут весьма существенно и необходимости в их просмотре при каждой загрузке страницы нет. Для их просмотра используются 3 js библиотеки. Чтобы, без необходимости, не загружать не используемые .js библиотеки, реализуется загрузка скриптов на страницу только по запросу клиента на использование функций страницы, требующей этих скриптов. Файлы 3D моделей загружаются только когда клиент изъявил желание взглянуть на конкретную 3D модель. Т.е. отображается отрендеренное зображение 3D модели, по клику на этом изображении, загружаются, если не были загружены, нужные скрипты, файл данных 3D модели, и запускается функция отображения 3D модели подменяющая текущее изображение. Всё кэшируется.

Для загрузки скриптов по требованию есть разннообразные решения. Выбрал возможно не оптимальное, но железно работающее. Использую jQuery включающую только ajax функции. В минифицированном и gzip ованном виде занимает около 22k. Да можно найти или написать решение весящее именее 3k.

Соответственно подключаем один единсвенный .js скрипт и в конце body:


...
<script type="text/javascript" src="../JSC/jqueryajaxmin.js"></script>
<script type="text/javascript">
...
</script>
</body>

jqueryajaxmin.js -это jquery библиотека сгенерированная online и содержащая только ajax.

Далее, создаём в нужном месте canvas, через css задаём размеры и пр, фоном задаём отрендеренное изображение соответствующего 3D элемента.


...
<canvas id="pkrate" class="lcolcnv"></canvas>
...

В разделах "JSC3D произвольное изменение zoom фактора отображаемого объекта с сохранением при перезагрузке", "JSC3D сохранение ориентации 3D модели при перезагрузке страници", "JSC3D сохранение текущих координат отображаемой 3D модели при перезагрузке страницы" описано в деталях, с примером рабочего кода, что и как должно быть, для универсального способа решения задачи. Т.е. когда на странице много canvas в которых будут отображаться, по клику, 3D модели, причём при при перезагрузке страницы установленные клиентом ориентация, масштаб и координаты 3D модели будут сохраняться.

JSC3D произвольное изменение zoom фактора отображаемого объекта с сохранением при перезагрузке

Zoom фактор JSC3D можно менять только после полной загрузки модели в canvas иначе изменение не произойдёт. Отображение модели в canvas и смена zoom фактора: cначала загружается фон, затем появляется статус бар, после загрузки 3D модели она отображается в canvas. Статус бар JSC3D по умолчанию включен. Далее меняем .zoomFactor с последующим .update();. Использование временной задержки для ожидания загрузки и отображения 3D модели в canvas не серьёзно. Загрузка больших моделей может производиться довольно долго. В jsc3d.js есть ".isLoaded" булевый флаг сигнализирующий об окончании загрузки 3D модели, его и используем. Чтобы менять zoom фактор на нужный, например, после загрузки страницы: периодически опрашиваем .isLoaded флаги всех экземпляров JSC3D.Viewer объектов, имеется в виду, что на странице несколько canvas с отображаемыми 3D объектами. Как только 3D изображение прогрузится в какой либо из canvas, то jsc3d.js установит .isLoaded в true и только тогда меняем zoom фактор на нужный с последующим .update(); Код реализующий вышеописанное

В вышеприведённом коде есть много лишнего, осталось от экспириментов, не убирал для достоверности. Ниже будет приведён полноценный код со всем обещанным.

localstorage:

localstorage хранит только строки, поэтому всё, что в него хотим сохранить преобразуем в строку при помощи JSON.stringify(), а всё что хотим извлеч парсим JSON.parse().

JSON.stringify() https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify

JSC3D сохранение ориентации 3D модели при перезагрузке страницы

В jsc3d.js нет функции получения текущей ориентации модели. Есть только встроенная функция инкриментного вращения ".rotate(x, y, z);". В jsc3d.js вращение производится через матрицу вращения и из неё можно извлечь те самые углы. Углы будут соответствовать действительности, но диапазоны их изменеия не совсем привычные, особенно для Z. Ниже приведён такой алгоритм:

//jsc3d.js функция вычисления текущего угла поворота модели в градусах, относительно X, Y, Z осей
//Диапазон оборота в градусах, для X{0 180 -180 0}, Y{0 90 0 -90 0}, Z{0 180 180 0 и -180 180} по Z градусы меняются в зависимости от X и Y на глаз непонятным образом, нужно разбираться
//Все значения выдаются правильные, но для преобразования их к привычным диапазонам нужен дополнительный алгоритм. Полученые значения можно использовать в штатной функции rotate
//Формуля для вычисления углов по данным матрицы вращения взяты из соответствующего справочного материала
JSC3D.Viewer.prototype.getRotationAngles = function() {
var angles = [];
angles[0] = Math.atan2(this.rotMatrix.m21, this.rotMatrix.m22);
angles[1] = Math.atan2(-this.rotMatrix.m20, Math.sqrt(this.rotMatrix.m00*this.rotMatrix.m00 + this.rotMatrix.m10*this.rotMatrix.m10));
angles[2] = Math.atan2(Math.sin(angles[0])*this.rotMatrix.m02 - Math.cos(angles[0])*this.rotMatrix.m01, Math.cos(angles[0])*this.rotMatrix.m11 - Math.sin(angles[0])*this.rotMatrix.m12 );
angles[0] = angles[0] * 180 / Math.PI; //rotX
angles[1] = angles[1] * 180 / Math.PI; //rotY
angles[2] = angles[2] * 180 / Math.PI; //rotZ
return angles;
};

Функция getRotationAngles -это метод, добавляемый к объекту JSC3D.Viewer. В jsc3d.js его прописывать не нужно, добавляется в тело разрабатываемого скрипта, можно сразу после подключения jsc3d.js. Алгоритм написан по информации из соответствующего справочного материала, его работоспособность проверена и подтверждена, о чём свидетельствует правильность сохранения и восстановления ориентации модели при перезагрузке страницы т.е. получаемые углы от разработанной ".getRotationAngles();" функции, при подстановке в ".rotate(x, y, z);" штатную функцию дают одинаковое отображение ориентации 3D модели. Но для представления углов в удобных для восприятия диапазонах нужен ещё один алгоритм. В нём пока необходимости не возникло.

Для решения текущей задачи -сохранеие ориентации модели в пространстве, при перезагрузке страницы подобной функциональности достаточно. Ориентация читается jsc3d.js ".getRotationAngles();" функцией, сохраняется через localStorage, восстанавливается ".rotate(x, y, z);"

JSC3D сохранение panning текущих координат отображаемой 3D модели при перезагрузке страницы

Центральным объектом в jsc3d является сцена, на которой можно размешать разное количество children mesh -дочерние 3D элементы. В рассматриваемом случае на сцене 1 3D элемент, загруженный из .obj файла. Файл 3D модели создан во FreeCAD импортированного в Blender, исправлен масштаб, убрано лишнее и экспортирован в .obj файл демонстрируемый на web странице. Из FreeCAD в Blender передавать лучше через .stl сохраняется масштаб.

Panning нашол только у "JSC3D.Viewer()" создаваемых экземплятов. У children этого параметра не увидел. Другими словами без приключений перемещать по canvas можно только сцену в целом, одновременно со всеми children. Panning -двухэлементный массив X Y положения сцены в пикселах относительно центра canvas.

Можно отключать видимость каждого из children элементов. Но если нужно перемещать и позиционировать в пределах canvas независимо несколько объектов, то можно один canvas наложить на другой с установкой разных z-index. Например первый canvas с одной 3D моделью и фоном, последующие с другими 3D моделями c прозрачным фоном. Background: 'off' при инициализации new JSC3D.Viewer() задаёт прозрачный фон. Таким образом можно независимо перемещать и позиционировать в пределах canvas несколько 3D объектов, которые могут состоять из разного количества children элементов. Желательно дополнительно реализовать более удобное вращение.

Собственно сохранение восстановление производится аналогично вышеописанным способом. Итоговый скрипт

Вращение 3D модели в canvas

Ничего придумывать не нужно, всё есть:

Кусок кода из класса отображения 3D объекта

//canvas отображения 3D кнопки сброса
this.br = new JSC3D.Viewer(document.getElementById('canvas ID'), {
SceneUrl: '../m/reset3db.stl',
InitRotationX: -90,
InitRotationY: 10,
InitRotationZ: 10,
CreaseAngle: 30,
Background: 'off',
//BackgroundColor1: '#FFFFFF',
//BackgroundColor2: '#383840',
RenderMode: 'texturesmooth',
Renderer: 'webgl'
});
this.br.init();
this.br.setDefinition('low');
this.br.update();

this.br.onloadingcomplete = function()
{
k.br.setMouseUsage('rotate');
k.br.zoomFactor = k.br.zoomFactor*2.2;
k.br.update();
setInterval(function(){ //Вращение кнопки reset
k.br.rotate(0, -5, 0); //x y z приращения в градусах по осям вращения
k.br.update();
}, 100); //100 миллисекунд -интервал между приращениями
}

Вращает функция .rotate(), в ней указывается только приращение по соответствующей оси, скорость вращения задаём через setInterval:

setInterval(function(){    //Вращение кнопки reset
k.br.rotate(0, -5, 0); //x y z приращения в градусах по осям вращения
k.br.update();
}, 100); //100 миллисекунд -интервал между приращениями

Добавление активной кнопки сброса 3D изображения в дефолтное. Ограничение zoom фактора min max значениями

Активную кнопку можно добавить несколькими способами. Можно через рисование кнопки в 2D области того же canvas с добавлением события клика на области. Но в качестве кнопки попробовал использовать другой canvas наложенный на основной. Такой подход очень гибкий, в области canvas кнопки можно разместить всё, что угодно, в том числе и ещё одно 3D изображение, что и было сделано, причём 3D модель кнопки была сделана врвщающейся. Далее ставим обработчик на клик по canvas кнопке. Если использовать в качестве кнопки вращающуюся 3D модель, то нужно позаботиться, чтобы пользователь не мог увеличивать масштаб до бесконечности, иначе это сильно нагрузит процессор клиента.

this.br.onmousedown = function() //обработка клика по canvas области/кнопке
{
...
}

Код ограничения zoom минимальным и максимальным значениями масштаба:

//zoom нельзя увеличить/уменьшить больше/меньше заданного значения
this.br.onmousewheel = function()
{
var d = this.scene.aabb.lengthOfDiagonal();
var z = (this.frameWidth < this.frameHeight ? this.frameWidth : this.frameHeight) / d;
var max_scale_from_z = 4; //Максимальный zoom масштаб от оригинального масштаба z при инициализации
var min_scale_from_z = 1.5; //Минимальный zoom масштаб от оригинального масштаба z при инициализации
if(this.zoomFactor/z > max_scale_from_z) this.zoomFactor = z*max_scale_from_z;
if(this.zoomFactor/z < min_scale_from_z) this.zoomFactor = z*min_scale_from_z;
}

Наложение canvas один на другой даёт возможность на одном canvas отображать несколько независимо позиционируемых 3D объекта и много дополнительных возможностей.

Очистка canvas от 2d содержимого

У меня webgl не поддерживается. Потребовалось по нажатию на кнопку очищать всё содержимое canvas, 3D и 2D контент. Как понял, 3D контент, при отсутствии webgl эмулируется при помощи 2D jsc3d.webgl.js.

Очистка 2D содержимого canvas:

//Очистка 2D контента canvas
function clearCanvas(id){
var el = document.getElementById(id);
var ctx = el.getContext('2d');
ctx.beginPath();
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, el.width, el.height);
ctx.restore();
}

Втотрой способ очистки canvas:

canvas.width = canvas.width;

Этот способ более медленный, сбрасывается всё состояние canvas. jsc3d.webgl.js библиотека jsc3D для браузеров не поддерживающих webgl.

Но в итоге очистка была реализована перезагрузкой страницы. Иначе нужно останавливать врвщающиеся 3D модели, остановка setInterval через асинхронную clearInterval(), отловить окончание работы которой никак. Далее очищать canvas в текущем случае 2D контент. А что если webgl у клиента поддерживается и нужно учесть как в этом случае очищать canvas, ко всему нужно память очищать от неиспользуемого объекта. И мало ли чего ещё, поэтому очистка была реализованна через перезагрузку с кэша браузера клиента, с возвращением на позицию клиента.

Асинхронная загрузка зависимых скриптов по запросу клиента

На тему существует и множится огромное количество материала. Есть готовые загрузчики. Нужен асинхронный загрузчик с сохранением порядка запуска скриптов с callback.

Скрипты нужно загружать асинхронно. Т.е. одновременно и в несколько потоков. Когда и в какой очерёдности скрипты подгрузятся не известно. Обычно есть зависимые друг от друга скрипты + после загрузки содержимое тут же выполняется. Отсюда проблема, если не загрузился скрипт, от которого зависит выполняемый, то получим ошибки. antjsc3d.js использует jQuery для удобства. Проблема возникает не всегда, способ загрузки рекомендуемый Google. При быстром двойном нажатии на F5 возникает ошибка -невозможность распознавания селектора $, т.е. jQuery ещё не подгрузился. При нажатии с перерывом в несколько секунд всё в порядке. Рекомендуемый Google способ загрузки скриптов:

function downloadJSAtOnload() 
{
var jsFiles = ['/j/jsc3d.js', '/j/jsc3d.touch.js', '/j/jsc3d.webgl.js', '/j/jquery-1.12.0.js', '/j/antjsc3d.js'];
var fileAmount = jsFiles.length;
for (var i = 0; i < fileAmount; i++) {
var element = document.createElement("script");
element.src = jsFiles[i];
document.body.appendChild(element);
}
}
if (window.addEventListener) window.addEventListener("load", downloadJSAtOnload, false);
else if (window.attachEvent) window.attachEvent("onload", downloadJSAtOnload);
else window.onload = downloadJSAtOnload;

Этот способ позволяет загружать скрипты асинхронно, но последовательность их выполнения никак не контролируется. Нижеприведённый скрипт, не мой, решает эту задачу:


...
CanvasConfig = ...
...
var Loader = function () { }
Loader.prototype = {
require: function (scripts, callback) {
this.loadCount = 0;
this.totalRequired = scripts.length;
this.callback = callback;

for (var i = 0; i < scripts.length; i++) {
this.writeScript(scripts[i]);
}
},
loaded: function (evt) {
this.loadCount++;

if (this.loadCount == this.totalRequired && typeof this.callback == 'function') this.callback.call();
},
writeScript: function (src) {
var self = this;
var s = document.createElement('script');
s.type = "text/javascript";
s.async = true;
s.src = src;
s.addEventListener('load', function (e) { self.loaded(e); }, false);
var head = document.getElementsByTagName('head')[0];
head.appendChild(s);
}
}


var l = new Loader();
l.require([
"/j/jquery-1.12.0.js",
"/j/antjsc3d.js"],
function() {
ant.init(CanvasConfig);
});

Скрипт от Chris Coyier https://css-tricks.com/snippets/javascript/async-script-loader-with-callback/ реализует асинхронную загрузку с сохранением порядка запуска скриптов, поддерживает вызов callback функции.

Вышеприведённый скрипт асинхронно загружает 2 .js файла запускает их в последовательности, указанной при загрузке и через callback запускает ant.init().

Помимо всего, в antjsc3d.js реализована подгрузка jsc3d.js jsc3d.touch.js jsc3d.webgl.js если пользователь кликнет по соответствующему canvas.у. От jQuery можно отказаться, или подгружать его также только по щелчку пользователя по canvas, для этого нужно подправить antjsc3d.js. Смысл в этом большой, поскольку отображение 3D изображения будет производиться на каждой странице сайта и клиенту лишнюю сотню килобайт, которая может не пригодится заливать не к чему. В конечном скрипте будет продемонстрирован подобный подход. Грузится только antjsc3d.js, его инициализирующая часть не содержит jQuery вызовов. После клика мышкой по canvas производится подгрузка 4х js скриптов, первым jQuery. И вторая часть antjsc3d.js, содержащая jQuery вызовы сможет работать.

Но в итоге для асинхронной загрузки использовался head.js

Конечный скрипт antjsc3d.js со всеми последними изменеиями

Как пользоваться:

antjsc3d.js открыт свободен, за последствия использования автор ответственности не несёт. Скрипт, отлажен для IE9, FireFox 40.x, Yandex, под оперу пока не "пошёл". Испытывался в среде без поддержки webgl.

Навигация по canvas.у:

  • Вращение -удержание левой кнопки мыши и перемещение
  • Масштаб -скроллинг колесом мыши или удержание SHIFT и перемещение мыши
  • Перемещение -удержание CTRL и перемещение мыши

Создаём в нужных местах страницы canvasы. В том числе в одном родительском элементе их можно создавать любое количество. Вложенные canvas стандартом не поддерживаются и не допустимы. Создавать canvas желательно в div, если создать в таблице, то после загрузки страницы в результате подгонки под текст может произойти смещение canvas, и кнопки останутся вне canvas, нужно будет "передёрнуть " масштаб, чтобы они встали на место. В дальнейшем, как руки дойдут, canvas в div будет оборачиваться автоматически.

<canvas id="testa" class="canv"></canvas>
<canvas id="testb" class="canv"></canvas>
<canvas id="testc" class="canv"></canvas>

css свойство position родительского/родительских элементов должно быть "relative". Float можно в left.

Задаём css класс canv для декору, не обязательно:

.canv { 
border-top-width: thin;
border-right-width: thin;
border-bottom-width: thin;
border-left-width: thin;
border-top-style: solid;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: solid;
cursor: crosshair;
}

Важные canv css параметры canvas.ов задаются в ant.scene функции автоматически, после загрузки документа, пользователю их настраивать не нужно.

Ниже описан код, добавляемый на html страницу. Приведён общий случай, когда сайт строится на базе шаблона, на котором есть canvas и в редактируемой области шаблона есть свои canvas.ы в функцию инициализации ant.init(CanvasConfig); должен попасть один общий массив с описанием настроек всех canvas на странице, поэтому используется конкатенация массивов CanvasConfig = CanvasConfig.concat(ucn);. Загрузка необходимых библиотек производится асинхронно и запуск в порядке загрузки, указанном в l.require.

В конце body добавляем(первый вариант):

<script type="text/javascript">
var CanvasConfig = [
{id: 'pkrate', url: '/m3d/antehru_text.stl', bkgurl: '/impic/Russia.png', w: 300, h: 150, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, breset_r: 'on', bclose_r: 'off', main_r: 'on'}, {id: 'pcnc6090', url: '/m3d/antehru_text.stl', bkgurl: '', w: 250, h: 125, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 3},
{id: 'test1', url: '/m3d/antehru_text.stl', bkgurl: '', w: 200, h: 100, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 0.5},
{id: 'test2', url: '/m3d/antehru_text.stl', bkgurl: '', w: 100, h: 50, rx: -56.5, ry: 1.6, rz: 20.8, panx: 10, pany: 10, zoomscale: 2, bclose_r: 'on', main_r: 'on'}
];
</script>

<!-- InstanceBeginEditable name="canvas_init" -->
<script type="text/javascript">
var ucn = [
{id: 'testa', url: '/m3d/antehru_text.stl', bkgurl: '/impic/Russia.png', w: 250, h: 125, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, breset_r: 'on', bclose_r: 'off', main_r: 'on', display3D: 'on'}, {id: 'testb', url: '/m3d/antehru_text.stl', bkgurl: '', w: 180, h: 90, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 3},
{id: 'testc', url: '/m3d/antehru_text.stl', bkgurl: '', w: 140, h: 70, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 0.5},
{id: 'testd', url: '/m3d/antehru_text.stl', bkgurl: '/impic/Russia.png', w: 100, h: 50, rx: -56.5, ry: 1.6, rz: 20.8, panx: 10, pany: 10, zoomscale: 2, bclose_r: 'on', main_r: 'on'},
{id: 'teste', url: '/m3d/antehru_text.stl', bkgurl: '', w: 160, h: 80, rx: -56.5, ry: 1.6, rz: 20.8, panx: 10, pany: 10, zoomscale: 2},
{id: 'testf', url: '/m3d/antehru_text.stl', bkgurl: '', w: 150, h: 75, rx: -56.5, ry: 1.6, rz: 20.8, panx: 10, pany: 10, zoomscale: 2, display3D: 'on'},
{id: 'testg', url: '/m3d/antehru_text.stl', bkgurl: '', w: 100, h: 50, rx: -56.5, ry: 1.6, rz: 20.8, panx: 10, pany: 10, zoomscale: 2},
{id: 'testh', url: '/m3d/antehru_text.stl', bkgurl: '/impic/Russia.png', w: 200, h: 100, rx: -56.5, ry: 1.6, rz: 20.8, panx: 10, pany: 10, zoomscale: 2}
];
CanvasConfig = CanvasConfig.concat(ucn);
</script>
<!-- InstanceEndEditable -->

<script type="text/javascript">
var Loader = function () { }
Loader.prototype = {
require: function (scripts, callback) {
this.loadCount = 0;
this.totalRequired = scripts.length;
this.callback = callback;
for (var i = 0; i < scripts.length; i++) {
this.writeScript(scripts[i]);
}
},
loaded: function (evt) {
this.loadCount++;
if (this.loadCount == this.totalRequired && typeof this.callback == 'function') this.callback.call();
},
writeScript: function (src) {
var self = this;
var s = document.createElement('script');
s.type = "text/javascript";
s.async = true;
s.src = src;
s.addEventListener('load', function (e) { self.loaded(e); }, false);
var head = document.getElementsByTagName('head')[0];
head.appendChild(s);
}};

var l = new Loader();
l.require([
"/JSC/jquery-1.12.0.min.js",
"/JSC/antjsc3d.js"],
function(){ ant.init(CanvasConfig); });
</script>

[] -значение по умолчанию

id -['idca'] id canvas.а

bkgurl -[''] фоновое изображение при отключённом canvas

w -[400] ширина canvas

h -[200] высота canvas

url -[''] url отображаемой модели относительно корня сайта. Можно использовать .obj и .stl. В итоге лучше .obj он раза в 2 меньше. Так же поддерживается ещё несколько форматов, но для их использования нужно подгрузить ещё библиотек. Обязательно на сервере должен быть включён gzip например через deflate. .obj сжимается раз в 5. Размеры исходного .obj файла могут быть мегабайты и десятки мегабайт. Для уменьшения, можно, например в Blender сделать соответствующие пребразования.

rx, ry, rz -[0, 0, 0] угловая ориентация модели по осям вращения. В текущем контексте это волшебно перпендикулярные значения, которые очень трудно сформулировать самостоятельно. Параметр debug: 'on' включит режим отображения этих значений над canvas 3D модели по клику кнопки.

panx, pany -[0, 0] координаты 3D модели относительно центра canvas

zoomscale -[1] вещественное число, на которое умножается дефолтный масштаб 3D модели

rotate-[off] 'off' или 'колличество миллисекунд' между приращениями по углам вращения. Задаёт скорость вращения 3D модели или кнопок. Вращение может существенно загружать процессор. Особенно в IE.

display3D -[off] 'off' или 'on' отображение 3D модели сразу же после загрузки страницы без необходимости клика по canvas

debug -[off] 'off' или 'on' режим отображения текущих углов вращения 3D модели в canvas для настройки rx, ry, rz угловой ориентации модели по осям вращения при инициализации страницы. Работает как для основного canvas, так и для кнопок.

rotx -[5] угол в градусах, на который раз в rotate миллисекунд будет изменяться положение 3D модели относительнооси X

roty -[5] угол в градусах, на который раз в rotate миллисекунд будет изменяться положение 3D модели относительнооси Y

rotz -[5] угол в градусах, на который раз в rotate миллисекунд будет изменяться положение 3D модели относительнооси Z

У кнопок нет параметра background и ID. Background отключен. ID, динимическим canvas.ам кнопки, задаётся динамически.

Если параметр не задан, то он будет инициализирован значением по умолчанию.

Это основные параметры, остальные правим прямо в скрипте, их слишком много и случаи бывают разные. Например, допустимые пределы изменения масштаба содержимого canvas, режим отображения 3D и пр.

На canvas.е динамически размещаются 2 кнопки: левая -сброс 3D изображения в дефолтное, правая замена 3D на background с полной очисткой памяти от отображаемого 3D изображения.

Соответственно в директории сайта /j должны быть следующие .js библиотеки:

jquery-1.12.0.js
antjsc3d.js
jsc3d.js
jsc3d.touch.js
jsc3d.webgl.js

Также нужно в antjsc3d.js подправить пути к jsc3d.js jsc3d.touch.js jsc3d.webgl.js согласно размещению на вашем сайте. Скрипты желательно минифицировать. Что есть что разбираемся по контексту.

Первый вариант скрипта antjsc3d.js

Второй вариант скрипта antjsc3d.js

На html странице размещаем, что к чему разбираемся по контексту:

<script type="text/javascript">
var CanvasConfig = [
{debug: 'on', id: 'pkrate', url: '/m3d/antehru_text.stl', bkgurl: '/impic/Russia.png', w: 300, h: 150, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, rotate: '200'},

{id: 'pcnc6090', url: '/m3d/antehru_text.stl', bkgurl: '', w: 250, h: 125, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 3,
breset: {url: '/m3d/reset.obj', postop: 20, posleft: 50, bkgurl: '/impic/Russia.png', w: 40, h: 90, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, rotate: 'off'},
bclose: {url: '/m3d/antehru_text.stl', postop: 5, posleft: 5, bkgurl: '/impic/Russia.png', w: 20, h: 20, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, rotate: 'off'} },

{id: 'test1', url: '/m3d/antehru_text.stl', bkgurl: '', w: 200, h: 100, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 0.5, display3D: 'on'},
{id: 'test2', url: '/m3d/antehru_text.stl', bkgurl: '/impic/Russia.png', w: 100, h: 50, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 4, rotate: '100',
breset: {url: '/m3d/reset.obj', postop: 5, posleft: 5, w: 30, h: 30, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, rotate: '100'},
bclose: {url: '/m3d/reset.stl', postop: 5, posleft: 40, w: 30, h: 30, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 1.5, rotate: '100'}}
];
</script>

<script type="text/javascript">
var ucn = [
{id: 'testa', url: '/m3d/antehrutr.obj', w: 400, h: 200, rx: -56.5, ry: 1.6, rz: 20.8, zoomscale: 2, display3D: 'on'},
{id: 'testb', url: '/m3d/antehrutr.obj', bkgurl: '/notes/diff/pic/notes_3dmodel_to_html_image001.jpg', w: 400, h: 200, rx: -56.5, ry: 1.6, rz: 20.8, zoomscale: 2},
{id: 'testc', url: '/m3d/antehrutr.obj', bkgurl: '/notes/diff/pic/notes_3dmodel_to_html_image001.jpg', w: 400, h: 200, rx: -56.5, ry: 1.6, rz: 20.8, panx: 0, pany: 0, zoomscale: 2,
breset: {url: '/m3d/brs.obj', postop: 5, posleft: 5, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: 30, ry: -14, rz: 51, zoomscale: 1.5},
bclose: {url: '/m3d/bcl.obj', postop: 5, posleft: 40, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: -5, ry: 14, rz: 175, zoomscale: 1.5} },

{id: 'testd', url: '/m3d/antehrutr.obj', bkgurl: '/notes/diff/pic/notes_3dmodel_to_html_image001.jpg', w: 400, h: 200, rx: -56.5, ry: 1.6, rz: 20.8, zoomscale: 2,
breset: {url: '/m3d/brs2.obj', postop: 5, posleft: 5, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: 30, ry: -14, rz: 51, zoomscale: 1.5, rotate: '100'},
bclose: {url: '/m3d/bcl.obj', postop: 5, posleft: 40, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: -5, ry: 14, rz: 175, zoomscale: 1.5} },

{id: 'teste', url: '/m3d/antehrutr.obj', bkgurl: '/notes/diff/pic/notes_3dmodel_to_html_image001.jpg', w: 400, h: 200, rx: -56.5, ry: 1.6, rz: 20.8, zoomscale: 2, rotate: '100',
breset: {url: '/m3d/brs.obj', postop: 5, posleft: 5, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: 30, ry: -14, rz: 51, zoomscale: 1.5, rotate: '100'},
bclose: {url: '/m3d/bcl.obj', postop: 5, posleft: 40, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: -5, ry: 14, rz: 175, zoomscale: 1.5, rotate: '100'} },
{id: 'testf', url: '/m3d/antehrutr.obj', bkgurl: '/notes/diff/pic/notes_3dmodel_to_html_image001.jpg', w: 400, h: 200, rx: -56.5, ry: 1.6, rz: 20.8, zoomscale: 2, debug: 'on',
breset: {url: '/m3d/brs.obj', postop: 5, posleft: 5, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: 30, ry: -14, rz: 51, zoomscale: 1.5, debug: 'on'},
bclose: {url: '/m3d/bcl.obj', postop: 5, posleft: 40, bkgurl: '/impic/Russia.png', w: 30, h: 30, rx: -5, ry: 14, rz: 175, zoomscale: 1.5, debug: 'on'} },
];
CanvasConfig = CanvasConfig.concat(ucn);
</script>

<script type="text/javascript">
var Loader = function () { }
Loader.prototype = {
require: function (scripts, callback) {
this.loadCount = 0;
this.totalRequired = scripts.length;
this.callback = callback;
for (var i = 0; i < scripts.length; i++) {
this.writeScript(scripts[i]);
}
},
loaded: function (evt) {
this.loadCount++;
if (this.loadCount == this.totalRequired && typeof this.callback == 'function') this.callback.call();
},
writeScript: function (src) {
var self = this;
var s = document.createElement('script');
s.type = "text/javascript";
s.async = true;
s.src = src;
s.addEventListener('load', function (e) { self.loaded(e); }, false);
var head = document.getElementsByTagName('head')[0];
head.appendChild(s);
}};

var l = new Loader();
l.require([
"/JSC/jquery-1.12.0.min.js",
"/JSC/antjsc3d.js"],
function(){ ant.init(CanvasConfig); });
</script>

</body>
</html>

Второй вариант скрипта antjsc3d.js

Краткое описание работы antjsc3d.js скрипта:

При загрузке страницы происходит асинхронная подгрузка jQuery и antjsc3d, запускается ant.init(CanvasConfig); который производит первоначальную инициализацию отображения canvas: задаёт ширину высоту background изображение, если установлен display3D флаг то инициирует загрузку соответствующего 3D изображения сразу. А также устанавливает обработчик события на клик мышкой по каждому canvas, описанному в CanvasConfig. Далее, либо всё замирает и ждёт пока пользователь кликнет мышкой по canvas, либо, если через localStorage был установлен флаг необходимости автоматического включения canvas: "localStorage.setItem(k.id, JSON.stringify(k.id));" производится автоматическая асинхронная подгрузка jsc3d библиотек. После загрузки производится автоматическая инициализация через искусственный клик мышкой нескольких canvas или одного canvas по которому кликнул пользователь.

Кнопки реализованны, как canvas области соответствующего размера и находящиеся над основным canvas. Это решение временное, отрабатывалась возможность наложения нескольких canvas друг на друга с независимым отображением и позиционированием 3D моделей. Практичнее реализовать кнопки в 2D области того же самого canvas. Кнопки больше для декору, без них вполне можно обойтись. Чтобы отключить кнопку её можно просто не описывать в конфигурации canvas.

Состояние canvas -отображение фона или 3D, ориентация и положение 3D модели через localStorage сохраняет своё состояние при перезагрузке страницы и при переходе между страницами, пока запущен хоть один экземпляр браузера одного типа. Это нужно, чтобы 3D содержимое canvas, настроенное клиентом, не меняло своего вида при перезагрузке и на разных страницах в течении всей сессии с клиентом. ЗАЧЕМ, а так захотелось и смоглось.

Чтобы сделать скрипт более "юзабельным", можно добавить визуализацию "кликабельности" кнопкам, сделать ориентацию модели в пространстве более удобной, добавить оси, некоторую текстовую информацию, реализовать класс для кнопки с наиболее используемыми параметрами. Сейчас кнопки добавлены каждая по отдельости с повторяющимся кодом это позволяет настроить любые параметры кнопки индивидуально -нужно для демонстрации некоторых возможностей. Но кнопка должна быть кнопкой и излишнее визуальное оформление ни к чему. Кнопки нужно реализовать в 2D области того же canvas. Добавить возможность динамического изменения размера canvas области просмотра 3D. Отключать вращение 3D, при уходе canvas из области видимости клиента, если оно было включено. Нужно добавить функцию препятствующую использовать 3D модели пользовтелем, т.к. они полностью подгружаются на клиентский компьютер. Нужно приостанавливать 3D анимацию, если canvas "ушёл" из области видимости пользователя. Отключение canvas нужно реализовать не через перезагрузку страницы. Оперативной памяти клиента потребляется прилично, "сборщик мусора" работает как положено, но медленно. Если canvas с 3D много, с десяток, они включены, и начать часто перезагружать страницу, то происходит временный большой расход памяти, пока сработает сборщик. Если есть поддержка webgl и кнопки реализовать через 2D область того же canvas, то будет существенно лучше, на сколько, если в граммах, нужно проверять.

Нагрузка на процессор, при просмотре 3D модели в canvas не пропадает, если canvas "ушёл" с области видимости.

Если хотите узнать, что и как можно сделать с jsc3D, то помимо изучения примеров и документации, рассматривайте DOM через отладчик браузера, например IE представляет всё в виде дерева. В "new JSC3D.Viewer(document.getElementById(this.id)" есть вся необходимая информация о функциях и параметрах поддерживаемых объектом.

Здесь пример использования jsc3d: http://www.columbia.edu/~fl2335/

Используя js3d можно отображать сборки с возможностью включения/отключения видимости элементов сборки. Даже без "допиливания", возможностей jsc3d.js хватает для качественного отображения 3D моделей на web страницах. Можно полноценный online 3D viewer создать и даже на online аналог Blender замахнуться:) Возможностей для расширений текущего скрипта много.

Copyright ©Новиков Алексей Александрович,

2025 Санкт-Петербург, 197372, ООО "Антех ПСБ",

anteh собака bk.ru, телеграм: собака antehru