-
-
Save s3rgeym/82fa775f68953790479c8874d6684cff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<template> | |
<SectionWrapper> | |
<SectionContainer class="text-center"> | |
<v-row> | |
<v-col cols="12"> | |
<h2 class="text-h2">Наши партнеры</h2> | |
</v-col> | |
<v-col cols="12"> | |
<p class="text-h4">Компании, которые нам доверяют.</p> | |
</v-col> | |
</v-row> | |
<v-row> | |
<v-col cols="12"> | |
<v-sheet> | |
<canvas id="canvas" width="600" height="300"></canvas> | |
</v-sheet> | |
</v-col> | |
</v-row> | |
</SectionContainer> | |
</SectionWrapper> | |
</template> | |
<script> | |
import { loadLogos } from '@/assets/logos' | |
class Point { | |
constructor(x, y) { | |
this.x = x | |
this.y = y | |
} | |
} | |
class Point3D extends Point { | |
constructor(x, y, z) { | |
super(x, y) | |
this.z = z | |
} | |
} | |
class LogoItem { | |
constructor(image, position) { | |
this.image = image | |
this.position = position | |
} | |
} | |
export default { | |
data() { | |
return { | |
canvas: null, | |
canvasContext: null, | |
logoItems: [], | |
camera: { position: { x: 0, y: 0, z: -500 }, focalLength: 500 }, | |
radius: 600, | |
rotationSpeed: 0.005, | |
tiltAngle: -Math.PI / 30 | |
} | |
}, | |
async mounted() { | |
this.canvas = document.getElementById('canvas') | |
this.canvasContext = this.canvas.getContext('2d') | |
const logos = await loadLogos() | |
// Загрузка изображений логотипов | |
const loadImage = (logo) => { | |
return new Promise((resolve, reject) => { | |
const img = new Image() | |
img.src = logo | |
img.onload = () => resolve(img) | |
img.onerror = () => | |
reject(new Error(`Failed to load logo image: ${logo}`)) | |
}) | |
} | |
const logoImages = await Promise.all(logos.slice(0, 20).map(loadImage)) | |
// Распределение логотипов по окружности на плоскости | |
const angleStep = (2 * Math.PI) / logoImages.length | |
for (let i = 0; i < logoImages.length; i++) { | |
const angle = i * angleStep | |
const x = this.radius * Math.cos(angle) | |
const z = this.radius * Math.sin(angle) | |
const y = this.radius * Math.sin(this.tiltAngle) * Math.sin(angle) // Учитываем наклон плоскости | |
const point = new Point3D(x, y, z) | |
this.logoItems.push(new LogoItem(logoImages[i], point)) | |
} | |
// console.log(this.logoItems) | |
this.startAnimation() | |
}, | |
methods: { | |
startAnimation() { | |
const animate = () => { | |
this.updatePositions() | |
this.drawScene() | |
requestAnimationFrame(animate) | |
} | |
animate() | |
}, | |
updatePositions() { | |
// Вращение всех логотипов вокруг оси Y | |
for (const item of this.logoItems) { | |
const { x, z } = item.position | |
const angle = Math.atan2(z, x) + this.rotationSpeed | |
item.position.x = this.radius * Math.cos(angle) | |
item.position.z = this.radius * Math.sin(angle) | |
item.position.y = | |
this.radius * Math.sin(this.tiltAngle) * Math.sin(angle) // Обновляем координату Y для наклона | |
// console.log(item) | |
} | |
}, | |
project3DTo2D(point) { | |
// console.log(`convert to 2d: ${JSON.stringify(point)}`) | |
const relativeX = point.x - this.camera.position.x | |
const relativeY = point.y - this.camera.position.y | |
const relativeZ = point.z - this.camera.position.z | |
// console.log(`relative z: ${relativeZ}`) | |
// Проекция 3D в 2D | |
const scale = | |
this.camera.focalLength / (this.camera.focalLength + relativeZ) | |
// console.log(`scale: ${scale}`) | |
const x2d = relativeX * scale + this.canvas.width / 2 | |
const y2d = relativeY * scale + this.canvas.height / 2 | |
return new Point(x2d, y2d) | |
}, | |
drawScene() { | |
const ctx = this.canvasContext | |
ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) | |
// Отрисовка объектов с учетом их глубины и изменения масштаба | |
for (const { image, position } of this.logoItems.sort( | |
(a, b) => b.position.z - a.position.z | |
)) { | |
const p0 = this.project3DTo2D(position) | |
// Определяем ширину и высоту картинки с учетом расстояния от камеры | |
const p1 = this.project3DTo2D( | |
new Point3D( | |
position.x + image.naturalWidth, | |
position.y + image.naturalHeight, | |
position.z | |
) | |
) | |
const scaledWidth = p1.x - p0.x | |
const scaledHeight = p1.y - p0.y | |
// console.log(scaledWidth, scaledHeight) | |
ctx.drawImage( | |
image, | |
p0.x - scaledWidth / 2, | |
p0.y - scaledHeight / 2, | |
scaledWidth, | |
scaledHeight | |
) | |
} | |
} | |
} | |
} | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment