Задача №2
Байрактар. З верхньої частини екрану у випадковій позиції по горизонталі з’являються танки, які їдуть вниз. При кліку на танк він вибухає і зникає з екрану.
Рішення:
'use strict'
class Tank {
className
options
leftPx = 0
topPx = 0
intervalId = null
/**
* @param {string} cssSelector
*/
constructor(cssSelector, options = {}) {
this.cssSelector = cssSelector
this.options = {
className: 'tank',
stepPx: 120,
speedS: 1,
removeDelayS: 3,
hull: {
className: 'tank__hull',
imgSrc: './images/tank_hull.webp',
widthPx: 40,
},
turret: {
className: 'tank__turret tank__turret--offset--regular',
imgSrc: './images/tank_turret.webp',
widthPx: 30,
},
bang: {
className: 'tank__bang',
imgSrc: './images/bang.webp',
widthPx: 180,
},
...options,
}
this.init()
}
setInitPosition() {
this.leftPx =
(document.documentElement.clientWidth - this.options.hull.widthPx) *
Math.random()
this.topPx = 0
}
updatePosition() {
this.topPx += this.options.stepPx
}
updatePositionUI() {
this.$el.style.left = this.leftPx + 'px'
this.$el.style.top = this.topPx + 'px'
if (this.topPx >= document.documentElement.clientHeight)
setTimeout(() => this.remove(), this.options.speedS * 1000 * 2)
}
/**@param {PointerEvent} e */
handleClickToDestroy({offsetX, offsetY}) {
this.stop()
this.$el.classList.add('has-blown-up')
setTimeout(() => this.$el.remove(), this.options.removeDelayS * 1000)
const bangEl = this.renderBang(offsetX, offsetY)
bangEl.addEventListener('animationend', (e) => {
e.stopPropagation()
bangEl.remove()
})
this.$el.append(bangEl)
}
/**
* @param {number} leftPx
* @param {number} topPx
*/
renderBang(leftPx, topPx) {
const bangEl = document.createElement('IMG')
bangEl.className = this.options.bang.className
bangEl.src = this.options.bang.imgSrc
bangEl.alt = ''
bangEl.width = this.options.bang.widthPx
bangEl.style.top = topPx + 'px'
bangEl.style.left = leftPx + 'px'
return bangEl
}
renderTurret() {
const turretEl = document.createElement('IMG')
turretEl.src = this.options.turret.imgSrc
turretEl.alt = ''
turretEl.width = this.options.turret.widthPx
turretEl.className = this.options.turret.className
return turretEl
}
renderHull() {
const hullEl = document.createElement('IMG')
hullEl.src = this.options.hull.imgSrc
hullEl.alt = ''
hullEl.width = this.options.hull.widthPx
hullEl.className = this.options.hull.className
return hullEl
}
renderTank() {
const hullEl = this.renderHull()
const turretEl = this.renderTurret()
const tankEl = document.createElement('DIV')
tankEl.className = this.options.className
tankEl.style.transitionDuration = this.options.speedS + 's'
tankEl.append(hullEl, turretEl)
return tankEl
}
start() {
if (this.intervalId) return
this.intervalId = setInterval(() => {
this.updatePosition()
this.updatePositionUI()
}, this.options.speedS * 1000)
}
stop() {
if (!this.intervalId) return
clearInterval(this.intervalId)
this.intervalId = null
}
remove() {
this.stop()
this.$el.remove()
}
render() {
this.$el = this.renderTank()
this.$el.addEventListener('click', this.handleClickToDestroy.bind(this))
this.setInitPosition()
this.updatePositionUI()
document.querySelector(this.cssSelector).append(this.$el)
return this.$el
}
init() {
this.render()
this.start()
}
}
// =============================================================================
for (let i = 0; i < 6; i++) {
setTimeout(() => new Tank('.js-app'), i * 900)
}
for (let i = 0; i < 5; i++) {
setTimeout(
() =>
new Tank('.js-app', {
stepPx: 140,
hull: {
className: 'tank__hull',
imgSrc: './images/pixel_tank_hull.webp',
widthPx: 45,
},
turret: {
className: 'tank__turret tank__turret--offset--pixel',
imgSrc: './images/pixel_tank_turret.webp',
widthPx: 30,
},
}),
i * 1600 + 900,
)
}
