Задача №1
Сяючі зорі. Вказана кількість зірочок повинна з’являтися у випадковій частині екрану. Кожна зірка (це екземпляр класу Star) з певним кроком й інтервалом збільшується від мінімального до максимального розміру. Як тільки досягнуто максимального розміру зірочка повинна з’являтися в іншій випадковій позиції.
Рішення:
'use strict'
function getRandomInteger(min, max) {
if (typeof min !== 'number' || typeof max !== 'number')
throw new TypeError('Min and max values must be numbers.')
return min + Math.floor(Math.random() * (max - min + 1))
}
class Star {
imgUrl
options
intervalId
#leftPx
#topPx
#sizePx
constructor(imgUrl, options = {}) {
this.imgUrl = imgUrl
this.options = {
cssClass: 'star',
minSizePx: 20,
maxSizePx: 60,
sizeStepPx: 25,
intervalMs: 300,
...options,
}
}
get sizePx() {
return this.#sizePx
}
set sizePx(newSizePx) {
this.#sizePx = newSizePx
this.$el.style.inlineSize = newSizePx + 'px'
}
get leftPx() {
return this.#leftPx
}
set leftPx(newLeftPx) {
this.#leftPx = newLeftPx
this.$el.style.left = newLeftPx + 'px'
}
get topPx() {
return this.#topPx
}
set topPx(newTopPx) {
this.#topPx = newTopPx
this.$el.style.top = newTopPx + 'px'
}
setRandomPosition() {
this.leftPx =
this.options.maxSizePx / 2 +
Math.random() *
(document.documentElement.clientWidth - this.options.maxSizePx)
this.topPx =
this.options.maxSizePx / 2 +
Math.random() *
(document.documentElement.clientHeight - this.options.maxSizePx)
}
resetSizePosition() {
this.sizePx = this.options.minSizePx
this.setRandomPosition()
}
run() {
if (this.intervalId) return
let isAboutToVanish = false
this.intervalId = setInterval(() => {
this.$el.style.opacity = ''
this.sizePx += this.options.sizeStepPx
if (isAboutToVanish) {
this.$el.style.opacity = '0'
isAboutToVanish = false
this.resetSizePosition()
return
}
if (this.sizePx >= this.options.maxSizePx) {
this.$el.style.opacity = '0'
isAboutToVanish = true
}
// just before the last tick, start to dim the star
}, this.options.intervalMs)
}
stop() {
if (!this.intervalId) return
clearInterval(this.intervalId)
this.intervalId = null
}
render(selector) {
/** @type {HTMLImageElement} */
this.$el = document.createElement('IMG')
this.$el.src = this.imgUrl
this.$el.alt = 'Зірка'
this.$el.className = this.options.cssClass
this.$el.style.transitionDuration = this.options.intervalMs + 'ms'
this.$el.style.opacity = '0'
this.resetSizePosition()
this.run()
if (selector) document.querySelector(selector).append(this.$el)
return this.$el
}
remove() {
this.stop()
this.$el.remove()
}
}
class StarSky {
stars = []
selector
options
constructor(selector, options = {}) {
this.selector = selector
this.options = {
cssClass: 'star',
minSizePx: 10,
maxSizePx: 40,
minSizeStepPx: 10,
maxSizeStepPx: 20,
minIntervalMs: 500,
maxIntervalMs: 800,
...options,
}
}
create(starAmount) {
if (typeof starAmount !== 'number')
throw new TypeError('Min and max values must be numbers.')
if (starAmount < 1) throw new RangeError('Stars amount must be positive.')
this.stars = Array.from(
{length: starAmount},
() =>
new Star('./images/star-eastern.svg', {
cssClass: this.options.cssClass,
minSizePx: this.options.minSizePx,
maxSizePx: this.options.maxSizePx,
sizeStepPx: getRandomInteger(
this.options.minSizeStepPx,
this.options.maxSizeStepPx,
),
intervalMs: getRandomInteger(
this.options.minIntervalMs,
this.options.maxIntervalMs,
),
}),
)
}
render() {
for (const star of this.stars) {
star.render(this.selector)
}
}
clear() {
for (const star of this.stars) {
star.remove()
}
}
}
// =============================================================================
try {
const starlight = new StarSky('.js-stars')
/** @param {SubmitEvent} e */
function handleStars(e) {
e.preventDefault()
const starsAmount = parseFloat(
e.currentTarget.querySelector('.js-stars-amount').value,
)
starlight.clear()
starlight.create(starsAmount)
starlight.render()
}
const starForm = document.querySelector('.js-stars-form')
starForm.addEventListener('submit', handleStars)
} catch (error) {
console.error(error)
}
