Skip to content

Instantly share code, notes, and snippets.

@s3rgeym
Created August 28, 2024 19:58
Show Gist options
  • Save s3rgeym/82fa775f68953790479c8874d6684cff to your computer and use it in GitHub Desktop.
Save s3rgeym/82fa775f68953790479c8874d6684cff to your computer and use it in GitHub Desktop.
<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