Как добавить 3d фото в карточку товара

--- ## Добавление 3D-фото в карточку товара InSales Руководство по интеграции интерактивных 3D-моделей товаров на страницы интернет-магазина с использованием библиотеки Three.js. --- ### Описание функционала **3D-визуализация товаров** — современный способ презентации продукции, позволяющий покупателям рассматривать товар со всех сторон, вращать и масштабировать модель прямо в браузере. **Преимущества 3D-моделей:** - Повышение вовлеченности покупателей - Снижение количества возвратов - Детальное изучение товара без физического контакта - Конкурентное преимущество перед обычными фотографиями - Улучшение пользовательского опыта **Примеры использования:** | Категория | Применение | |:----------|:-----------| | Мебель | Просмотр со всех сторон, оценка габаритов | | Электроника | Детальное изучение устройств | | Ювелирные изделия | Рассмотрение деталей, игра света | | Обувь и одежда | Проверка деталей, швов, фактуры | | Автозапчасти | Изучение конструкции и совместимости | --- ### Требования для реализации #### Технические требования | Компонент | Требование | |:----------|:-----------| | Платформа | InSales | | 3D-модель | Форматы: .glb, .gltf, .obj, .fbx | | Библиотека | Three.js (r128 или новее) | | Хостинг | Для размещения 3D-файлов | | Доступ | Редактирование темы оформления | | Браузеры | Поддержка WebGL | #### Поддерживаемые форматы 3D-моделей | Формат | Описание | Рекомендуется | |:-------|:---------|:--------------| | **.glb** | Бинарный формат glTF, компактный | ✅ Да | | **.gltf** | JSON-формат glTF с отдельными файлами | ✅ Да | | **.obj** | Старый формат, требует материалы отдельно | ⚠️ С ограничениями | | **.fbx** | Формат Autodesk, требует конвертации | ⚠️ С ограничениями | | **.wrl** | VRML формат (требует X3DOM) | ⚠️ Устаревший | > **Important:** Рекомендуется использовать формат **.glb** — он обеспечивает лучшее сжатие, быструю загрузку и включает текстуры в один файл. --- ### Подготовка 3D-модели #### Оптимизация модели Требования к файлу **Рекомендуемые параметры:** | Параметр | Значение | Причина | |:---------|:---------|:--------| | Размер файла | < 10 МБ | Быстрая загрузка | | Количество полигонов | < 100,000 | Производительность | | Размер текстур | 2048x2048 px | Баланс качества/размера | | Количество материалов | 1-3 | Оптимизация рендера | **Инструменты оптимизации:** - [Blender](https://www.blender.org/) — бесплатный 3D-редактор - [glTF Pipeline](https://github.com/CesiumGS/gltf-pipeline) — CLI для оптимизации glTF - [glTF Report](https://github.khronos.org/glTF-Validator/) — валидатор моделей **Пример оптимизации в Blender:** ```python # Скрипт для Blender: уменьшение полигонов import bpy # Выбор объекта obj = bpy.context.active_object # Добавление модификатора Decimate modifier = obj.modifiers.new(name="Decimate", type='DECIMATE') modifier.ratio = 0.5 # Уменьшение на 50% # Применение модификатора bpy.ops.object.modifier_apply(modifier="Decimate") ``` Конвертация в glTF/GLB **Способ 1: Через Blender** 1. Откройте 3D-модель в Blender 2. **File → Export → glTF 2.0 (.glb/.gltf)** 3. Настройте параметры экспорта: - Format: **glTF Binary (.glb)** - Include: **Selected Objects** - Transform: **+Y Up** - Geometry: **Apply Modifiers** - Compression: **Draco** (опционально) 4. Нажмите **Export glTF 2.0** **Способ 2: Онлайн-конвертер** - [gltf.report](https://gltf.report/) — онлайн просмотр и валидация - [Verge3D](https://www.soft8soft.com/verge3d/converter/) — конвертер форматов **Способ 3: CLI инструмент** ```bash # Установка gltf-pipeline npm install -g gltf-pipeline # Конвертация OBJ в GLB gltf-pipeline -i model.obj -o model.glb # Оптимизация с Draco сжатием gltf-pipeline -i model.glb -o model-optimized.glb -d ``` --- ### Размещение 3D-файла на хостинге #### Выбор хостинга Варианты хостинга **Бесплатные решения:** | Сервис | Ограничения | Прямые ссылки | |:-------|:------------|:--------------| | **GitHub** | 100 МБ на файл | ✅ Да (через raw.githubusercontent.com) | | **Google Drive** | 15 ГБ бесплатно | ⚠️ Требуется настройка | | **Dropbox** | 2 ГБ бесплатно | ✅ Да (с ?dl=1) | | **Cloudflare R2** | 10 ГБ бесплатно | ✅ Да | **Платные CDN (рекомендуется для продакшена):** - **Cloudflare CDN** — быстрая доставка по всему миру - **AWS S3 + CloudFront** — масштабируемое решение - **Bunny CDN** — доступные цены Загрузка на GitHub **Процесс загрузки:** 1. Создайте новый репозиторий на GitHub 2. Загрузите 3D-файл в репозиторий 3. Откройте файл и нажмите **Raw** 4. Скопируйте URL из адресной строки **Формат прямой ссылки:** ``` https://raw.githubusercontent.com/username/repository/main/model.glb ``` **Пример структуры репозитория:** ``` my-3d-models/ ├── furniture/ │ ├── chair.glb │ └── table.glb ├── electronics/ │ └── phone.glb └── README.md ``` > **Note:** GitHub имеет лимит 100 МБ на файл. Для больших моделей используйте GitHub LFS или другой хостинг. Настройка Google Drive **Получение прямой ссылки:** 1. Загрузите файл на Google Drive 2. Правый клик → **Открыть доступ** 3. Выберите **Доступен всем, у кого есть ссылка** 4. Скопируйте ID файла из URL: ``` https://drive.google.com/file/d/FILE_ID/view ``` 5. Сформируйте прямую ссылку: ``` https://drive.google.com/uc?export=download&id=FILE_ID ``` **Ограничения:** - Ограничение на количество скачиваний в день - Может не работать при большом трафике --- ### Реализация на InSales #### Шаг 1. Подключение библиотеки Three.js Добавление в scripts.liquid **Путь к файлу:** ``` Админ-панель InSales → Дизайн → Редактор темы → Шаблоны → scripts.liquid ``` **Добавьте в конец файла:** ```liquid {{ 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js' | script_tag }} ``` **Альтернативные версии:** ```liquid ``` > **Important:** Используйте стабильную версию библиотеки. Версия r128 проверена и совместима с примерами кода в этой инструкции. Подключение GLTFLoader **Для загрузки .glb/.gltf файлов:** ```liquid ``` **Для других форматов:** ```liquid ``` --- #### Шаг 2. Добавление контейнера в карточку товара Редактирование product.liquid **Путь к файлу:** ``` Админ-панель InSales → Дизайн → Редактор темы → Шаблоны → product.liquid ``` **Добавьте контейнер для 3D-модели:** ```html

3D-просмотр товара

Сбросить вид Автовращение
``` **Вариант с условным отображением:** ```liquid {% if product.field('3d_model_url') %}

Посмотреть в 3D

{% endif %} ``` > **Note:** Второй вариант требует создания дополнительного поля товара "3d_model_url" для хранения ссылки на модель. Создание дополнительного поля (опционально) **Для хранения URL модели в карточке товара:** ``` Админ-панель → Настройки → Дополнительные поля → Товары → Создать поле ``` **Параметры поля:** | Параметр | Значение | |:---------|:---------| | Название | URL 3D-модели | | Тип | Строка | | Handle | `3d_model_url` | | Обязательное | Нет | **Использование в теме:** ```liquid {{ product.field('3d_model_url') }} ``` --- #### Шаг 3. Создание скрипта 3D-визуализации Создание файла product_3d.js **Путь создания:** ``` Админ-панель InSales → Дизайн → Редактор темы → Ресурсы → Создать файл ``` **Название файла:** `product_3d.js` **Базовая версия кода:** ```javascript document.addEventListener('DOMContentLoaded', function() { const container = document.getElementById('product-3d'); if (!container) return; // URL модели (можно брать из data-атрибута или захардкодить) const modelUrl = container.dataset.modelUrl || 'URL_3D_ФАЙЛА'; // Создание сцены const scene = new THREE.Scene(); scene.background = new THREE.Color(0xf0f0f0); // Настройка камеры const camera = new THREE.PerspectiveCamera( 75, // Field of view container.clientWidth / container.clientHeight, // Aspect ratio 0.1, // Near plane 1000 // Far plane ); camera.position.set(0, 0, 5); // Создание рендерера const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.setPixelRatio(window.devicePixelRatio); container.appendChild(renderer.domElement); // Добавление освещения const ambientLight = new THREE.AmbientLight(0xffffff, 0.6); scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(10, 10, 10); scene.add(directionalLight); // Загрузка модели const loader = new THREE.GLTFLoader(); let model; loader.load( modelUrl, function(gltf) { model = gltf.scene; // Центрирование модели const box = new THREE.Box3().setFromObject(model); const center = box.getCenter(new THREE.Vector3()); model.position.sub(center); // Масштабирование под размер сцены const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const scale = 2 / maxDim; model.scale.setScalar(scale); scene.add(model); console.log('3D модель загружена успешно'); }, function(xhr) { // Прогресс загрузки const percentComplete = (xhr.loaded / xhr.total) * 100; console.log('Загрузка: ' + percentComplete.toFixed(2) + '%'); }, function(error) { console.error('Ошибка загрузки модели:', error); } ); // Анимация let autoRotate = false; function animate() { requestAnimationFrame(animate); if (model && autoRotate) { model.rotation.y += 0.01; } renderer.render(scene, camera); } animate(); // Адаптивность при изменении размера окна window.addEventListener('resize', function() { camera.aspect = container.clientWidth / container.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(container.clientWidth, container.clientHeight); }); }); ``` > **Important:** Замените `'URL_3D_ФАЙЛА'` на прямую ссылку на вашу 3D-модель. Расширенная версия с управлением **Продвинутый функционал:** ```javascript document.addEventListener('DOMContentLoaded', function() { const container = document.getElementById('product-3d'); if (!container) return; const modelUrl = container.dataset.modelUrl || 'URL_3D_ФАЙЛА'; // Инициализация сцены const scene = new THREE.Scene(); scene.background = new THREE.Color(0xf5f5f5); const camera = new THREE.PerspectiveCamera( 45, container.clientWidth / container.clientHeight, 0.1, 1000 ); camera.position.set(0, 2, 5); camera.lookAt(0, 0, 0); const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: true // Для скриншотов }); renderer.setSize(container.clientWidth, container.clientHeight); renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; container.appendChild(renderer.domElement); // Улучшенное освещение const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); scene.add(ambientLight); const mainLight = new THREE.DirectionalLight(0xffffff, 0.8); mainLight.position.set(5, 10, 7.5); mainLight.castShadow = true; scene.add(mainLight); const fillLight = new THREE.DirectionalLight(0xffffff, 0.3); fillLight.position.set(-5, 0, -5); scene.add(fillLight); // Добавление пола с тенью const planeGeometry = new THREE.PlaneGeometry(20, 20); const planeMaterial = new THREE.ShadowMaterial({ opacity: 0.2 }); const plane = new THREE.Mesh(planeGeometry, planeMaterial); plane.rotation.x = -Math.PI / 2; plane.position.y = -1; plane.receiveShadow = true; scene.add(plane); // Загрузка модели const loader = new THREE.GLTFLoader(); let model; let mixer; // Для анимаций // Индикатор загрузки const loadingDiv = document.createElement('div'); loadingDiv.id = 'loading-indicator'; loadingDiv.innerHTML = '

Загрузка 3D-модели...

'; loadingDiv.style.cssText = ` position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: #666; `; container.appendChild(loadingDiv); loader.load( modelUrl, function(gltf) { model = gltf.scene; // Включение теней model.traverse(function(child) { if (child.isMesh) { child.castShadow = true; child.receiveShadow = true; } }); // Центрирование и масштабирование const box = new THREE.Box3().setFromObject(model); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const scale = 2.5 / maxDim; model.scale.setScalar(scale); model.position.sub(center.multiplyScalar(scale)); model.position.y = 0; scene.add(model); // Обработка анимаций (если есть) if (gltf.animations && gltf.animations.length) { mixer = new THREE.AnimationMixer(model); gltf.animations.forEach(clip => { mixer.clipAction(clip).play(); }); } // Удаление индикатора загрузки loadingDiv.remove(); console.log('✓ 3D модель загружена'); }, function(xhr) { const percent = (xhr.loaded / xhr.total) * 100; loadingDiv.querySelector('p').textContent = `Загрузка: ${percent.toFixed(0)}%`; }, function(error) { console.error('Ошибка загрузки модели:', error); loadingDiv.innerHTML = '

Ошибка загрузки 3D-модели

'; } ); // Управление мышью (вращение) let isDragging = false; let previousMousePosition = { x: 0, y: 0 }; let rotationVelocity = { x: 0, y: 0 }; let autoRotate = true; renderer.domElement.addEventListener('mousedown', function(e) { isDragging = true; autoRotate = false; }); renderer.domElement.addEventListener('mousemove', function(e) { if (!isDragging || !model) return; const deltaMove = { x: e.offsetX - previousMousePosition.x, y: e.offsetY - previousMousePosition.y }; rotationVelocity.x = deltaMove.x * 0.01; rotationVelocity.y = deltaMove.y * 0.01; model.rotation.y += rotationVelocity.x; model.rotation.x += rotationVelocity.y; previousMousePosition = { x: e.offsetX, y: e.offsetY }; }); document.addEventListener('mouseup', function() { isDragging = false; }); // Управление колесом мыши (зум) renderer.domElement.addEventListener('wheel', function(e) { e.preventDefault(); const delta = e.deltaY * 0.01; camera.position.z = Math.max(2, Math.min(10, camera.position.z + delta)); }); // Кнопки управления const resetButton = document.getElementById('reset-camera'); if (resetButton) { resetButton.addEventListener('click', function() { camera.position.set(0, 2, 5); camera.lookAt(0, 0, 0); if (model) { model.rotation.set(0, 0, 0); } }); } const toggleRotationButton = document.getElementById('toggle-rotation'); if (toggleRotationButton) { toggleRotationButton.addEventListener('click', function() { autoRotate = !autoRotate; this.textContent = autoRotate ? 'Остановить' : 'Автовращение'; }); } // Анимация const clock = new THREE.Clock(); function animate() { requestAnimationFrame(animate); const delta = clock.getDelta(); // Обновление анимаций модели if (mixer) mixer.update(delta); // Автовращение if (model && autoRotate) { model.rotation.y += 0.005; } renderer.render(scene, camera); } animate(); // Адаптивность window.addEventListener('resize', function() { camera.aspect = container.clientWidth / container.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(container.clientWidth, container.clientHeight); }); }); ``` --- #### Шаг 4. Подключение скрипта к теме Добавление в scripts.liquid **Откройте файл scripts.liquid и добавьте в конец:** ```liquid {{ 'product_3d.js' | asset_url | script_tag }} ``` **Полный пример scripts.liquid:** ```liquid {{ 'theme.js' | asset_url | script_tag }} {{ 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js' | script_tag }} {{ 'product_3d.js' | asset_url | script_tag }} ``` --- ### Стилизация 3D-просмотрщика #### CSS для контейнера Добавление стилей **Добавьте в файл стилей темы:** ```css /* Контейнер 3D-просмотрщика */ .product-3d-viewer { margin: 30px 0; padding: 20px; background: #fff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); } .product-3d-viewer h3 { margin: 0 0 15px; font-size: 20px; font-weight: 600; color: #333; } /* Canvas контейнер */ #product-3d { width: 100%; height: 500px; border-radius: 8px; overflow: hidden; position: relative; background: linear-gradient(135deg, #f5f7fa 0%, #e4e8eb 100%); } #product-3d canvas { display: block; outline: none; } /* Кнопки управления */ .viewer-controls { display: flex; gap: 10px; margin-top: 15px; justify-content: center; } .viewer-controls button { padding: 10px 20px; background: #4CAF50; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; transition: background 0.3s; } .viewer-controls button:hover { background: #45a049; } .viewer-controls button:active { transform: scale(0.98); } /* Индикатор загрузки */ #loading-indicator { font-family: Arial, sans-serif; } #loading-indicator .spinner { width: 50px; height: 50px; margin: 0 auto 15px; border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } /* Адаптивность */ @media (max-width: 768px) { #product-3d { height: 300px; } .viewer-controls { flex-direction: column; } .viewer-controls button { width: 100%; } } ``` --- ### Дополнительные возможности #### Добавление OrbitControls Улучшенное управление камерой **Подключение библиотеки:** ```liquid ``` **Использование в коде:** ```javascript // После создания камеры и рендерера const controls = new THREE.OrbitControls(camera, renderer.domElement); // Настройка контролов controls.enableDamping = true; // Плавное затухание controls.dampingFactor = 0.05; controls.enableZoom = true; controls.enablePan = false; // Отключить панорамирование controls.minDistance = 2; controls.maxDistance = 10; controls.autoRotate = true; controls.autoRotateSpeed = 2.0; // В функции animate function animate() { requestAnimationFrame(animate); controls.update(); // Обновление контролов renderer.render(scene, camera); } ``` **Преимущества OrbitControls:** - Интуитивное управление мышью и тачскрином - Плавное вращение с инерцией - Автоматическое вращение - Зум с ограничениями - Минимум кода для реализации #### Поддержка разных форматов Работа с OBJ моделями **Подключение OBJLoader:** ```liquid ``` **Загрузка OBJ с материалами:** ```javascript const mtlLoader = new THREE.MTLLoader(); mtlLoader.load('model.mtl', function(materials) { materials.preload(); const objLoader = new THREE.OBJLoader(); objLoader.setMaterials(materials); objLoader.load('model.obj', function(object) { scene.add(object); }); }); ``` Работа с VRML (.wrl) **Подключение X3DOM:** ```html ``` **HTML разметка:** ```html ``` > **Note:** VRML — устаревший формат. Рекомендуется конвертировать в glTF. #### Оптимизация производительности Lazy loading 3D-модели **Загрузка при прокрутке:** ```javascript const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // Загружаем модель только когда контейнер виден loadModel(); observer.disconnect(); } }); }, { threshold: 0.1 }); observer.observe(container); ``` Прогрессивная загрузка **Показ низкополигональной версии сначала:** ```javascript // Загрузка легкой версии loader.load('model-low.glb', function(gltf) { const lowResModel = gltf.scene; scene.add(lowResModel); // Загрузка детальной версии в фоне loader.load('model-high.glb', function(gltf) { scene.remove(lowResModel); scene.add(gltf.scene); }); }); ``` --- ### Тестирование #### Проверка совместимости браузеров Поддержка WebGL **Проверка наличия WebGL:** ```javascript function checkWebGLSupport() { try { const canvas = document.createElement('canvas'); return !!( window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')) ); } catch(e) { return false; } } if (!checkWebGLSupport()) { document.getElementById('product-3d').innerHTML = `

Ваш браузер не поддерживает 3D-визуализацию

Пожалуйста, используйте современный браузер

`; } ``` **Поддерживаемые браузеры:** | Браузер | Минимальная версия | |:--------|:-------------------| | Chrome | 9+ | | Firefox | 4+ | | Safari | 5.1+ | | Edge | 12+ | | Opera | 12+ | | iOS Safari | 8+ | | Android Browser | 4.4+ | #### Чек-лист перед запуском **Функциональность:** - [ ] 3D-модель загружается корректно - [ ] Модель центрирована и масштабирована правильно - [ ] Работает вращение мышью/тачем - [ ] Функционирует зум колесиком/пинчем - [ ] Кнопки управления работают - [ ] Освещение настроено правильно - [ ] Нет ошибок в консоли браузера **Производительность:** - [ ] Размер файла модели < 10 МБ - [ ] FPS не падает ниже 30 на средних устройствах - [ ] Время загрузки < 5 секунд на 10 Мбит/с - [ ] Нет подтормаживаний при вращении **Кроссбраузерность:** - [ ] Работает в Chrome/Edge - [ ] Работает в Firefox - [ ] Работает в Safari (desktop) - [ ] Работает на iOS Safari - [ ] Работает на Android Chrome **Адаптивность:** - [ ] Корректное отображение на desktop - [ ] Корректное отображение на планшетах - [ ] Корректное отображение на мобильных - [ ] Управление тачем работает на мобильных --- ### Устранение типичных проблем | Проблема | Причина | Решение | |:---------|:--------|:--------| | Модель не загружается | Неверный URL или CORS | Проверьте URL, настройте CORS на хостинге | | Черный экран | Нет освещения | Добавьте AmbientLight и DirectionalLight | | Модель слишком большая/маленькая | Нет масштабирования | Используйте Box3 для расчета размеров | | Низкий FPS | Слишком много полигонов | Оптимизируйте модель, уменьшите полигоны | | Модель не в центре | Не рассчитан центр | Используйте Box3.getCenter() | | CORS ошибка | Хостинг блокирует запросы | Настройте Access-Control-Allow-Origin | | Текстуры не загружаются | Относительные пути в .gltf | Используйте .glb (все в одном файле) | | Не работает на мобильных | Не обработаны touch события | Добавьте обработчики touchstart/touchmove | --- ### Техническая поддержка > **Important:** Работа с 3D-графикой требует понимания основ WebGL и Three.js. При возникновении сложностей рекомендуется обратиться к специалистам. **Полезные ресурсы:** - [Документация Three.js](https://threejs.org/docs/) - [Примеры Three.js](https://threejs.org/examples/) - [Учебник по Three.js](https://threejs.org/manual/) - [glTF спецификация](https://www.khronos.org/gltf/) - [Blender документация](https://docs.blender.org/) **Сообщество:** - [Three.js Forum](https://discourse.threejs.org/) - [Stack Overflow: three.js](https://stackoverflow.com/questions/tagged/three.js) - [Three.js Discord](https://discord.gg/threejs) **Профессиональная помощь:** Обратитесь к специалистам **WS24.pro** для: - Создания и оптимизации 3D-моделей - Интеграции сложных 3D-сцен - Разработки кастомных 3D-визуализаций - AR/VR функционала для магазина --- > **Note:** Интеграция 3D-визуализации значительно повышает вовлеченность покупателей и может увеличить конверсию на 20-40% для товаров, требующих детального рассмотрения. Начните с нескольких ключевых товаров, оптимизируйте модели для быстрой загрузки, и постепенно расширяйте 3D-каталог на основе обратной связи от пользователей.