diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..96250dd --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,6 @@ +{ + "extends": "eslint-config-standard", + "rules": { + "no-unused-vars": "off" + } +} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c701365 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,24 @@ +name: CI + +on: + push: + branches: + - dev + pull_request: + branches: + - main + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Install dependencies + run: npm install + - name: Lint CSS + run: npm run lint:css + - name: Lint JS + run: npm run lint:js + - name: Run Prettier + run: npm run format \ No newline at end of file diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..d1aa6e1 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,9 @@ +{ + "extends": ["stylelint-config-standard"], + "rules": { + "selector-pseudo-class-no-unknown": null, + "selector-type-no-unknown": null, + "property-no-unknown": null, + "at-rule-no-unknown": null + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..fea8c34 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +[Ссылка на сайт](http://kolbaser.ru/) \ No newline at end of file diff --git a/content.css b/content.css new file mode 100644 index 0000000..12f6e86 --- /dev/null +++ b/content.css @@ -0,0 +1,194 @@ +/* stylelint-disable selector-class-pattern */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + background: #f1f4fd; +} + +.container { + max-width: 1200px; + width: 95%; +} + +.slider-wrapper { + position: relative; +} + +.slider-wrapper .slide-button { + position: absolute; + top: 50%; + outline: none; + border: none; + height: 50px; + width: 50px; + z-index: 5; + color: #fff; + display: flex; + cursor: pointer; + font-size: 2.2rem; + background: #000; + align-items: center; + justify-content: center; + border-radius: 50%; + transform: translateY(-50%); +} + +.slider-wrapper .slide-button:hover { + background: #404040; +} + +.slider-wrapper .slide-button#prev-slide { + left: -25px; + display: none; +} + +.slider-wrapper .slide-button#next-slide { + right: -25px; +} + +.slider-wrapper .image-list { + display: grid; + grid-template-columns: repeat(10, 1fr); + gap: 18px; + font-size: 0; + list-style: none; + margin-top: 100px; + margin-bottom: 30px; + overflow-x: auto; + scrollbar-width: none; +} + +.slider-wrapper .image-list::-webkit-scrollbar { + display: none; +} + +.slider-wrapper .image-list .image-item { + width: 325px; + height: 400px; + object-fit: cover; +} + +.container .slider-scrollbar { + height: 24px; + width: 100%; + display: flex; + align-items: center; +} + +.slider-scrollbar .scrollbar-track { + background: #ccc; + width: 100%; + height: 2px; + display: flex; + align-items: center; + border-radius: 4px; + position: relative; +} + +.slider-scrollbar:hover .scrollbar-track { + height: 4px; +} + +.slider-scrollbar .scrollbar-thumb { + position: absolute; + background: #000; + top: 0; + bottom: 0; + width: 50%; + height: 100%; + cursor: grab; + border-radius: inherit; +} + +.slider-scrollbar .scrollbar-thumb:active { + cursor: grabbing; + height: 8px; + top: -2px; +} + +.slider-scrollbar .scrollbar-thumb::after { + content: ""; + position: absolute; + inset: -10px 0; +} + +.return-btn { + position: fixed; + top: 20px; + left: 20px; + z-index: 1000; +} + +.return-btn img { + width: 50px; + height: auto; +} + +#scroll-button { + position: fixed; + top: 30px; + left: 96%; + transform: translateX(-50%); + background-color: transparent; + border: none; + cursor: pointer; +} + +#scroll-button img { + width: 50px; +} + +.video-container { + display: flex; + justify-content: center; + margin-top: 150px; + margin-bottom: 70px; +} + +video { + width: 80%; + max-width: 100%; + height: auto; +} + +@media only screen and (width <= 1023px) { + .slider-wrapper .slide-button { + display: none !important; + } + + .slider-wrapper .image-list { + gap: 10px; + margin-bottom: 15px; + scroll-snap-type: x mandatory; + } + + .slider-wrapper .image-list .image-item { + width: 280px; + height: 380px; + } + + .slider-scrollbar .scrollbar-thumb { + width: 20%; + } + + .video-container { + margin-top: 150px; + margin-bottom: 70px; + } + + #scroll-button { + left: 90%; + } + + video { + width: 100%; + } +} \ No newline at end of file diff --git a/content.html b/content.html new file mode 100644 index 0000000..50595ee --- /dev/null +++ b/content.html @@ -0,0 +1,59 @@ + + + + + + Котики!!! + + + + + + +
+ + Вернуться на главную + +
+ + + +
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+ +
+ +
+
+ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..de38ea6 --- /dev/null +++ b/index.html @@ -0,0 +1,47 @@ + + + + + + Про меня + + + + + + + + + + +
+
+
+

Артем

+

+ Привет, я учусь в ИТМО. Можете написать мне в + телеграм. +

+ +

+ Я очень люблю котиков, поэтому + тут + вы можете посмотреть видео и полистать фото c ними... +

+
+ +
+ Это я +
+
+
+ + diff --git a/package.json b/package.json new file mode 100644 index 0000000..25fbb47 --- /dev/null +++ b/package.json @@ -0,0 +1,20 @@ +{ + "name": "visit", + "version": "1.0.0", + "description": "[Ссылка на сайт](http://kolbaser.ru/)", + "main": "script.js", + "scripts": { + "lint:css": "stylelint --fix --ignore-path .gitignore *.css", + "lint:js": "eslint . --fix", + "format": "prettier --write \"**/*.html\" \"**/*.json\"" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "eslint": "^8.57.0", + "eslint-config-standard": "^17.1.0", + "prettier": "^3.2.5", + "stylelint": "^16.2.1", + "stylelint-config-standard": "^36.0.0" + } +} diff --git a/res/1.jpg b/res/1.jpg new file mode 100644 index 0000000..4253de7 Binary files /dev/null and b/res/1.jpg differ diff --git a/res/10.jpg b/res/10.jpg new file mode 100644 index 0000000..87e126a Binary files /dev/null and b/res/10.jpg differ diff --git a/res/2.jpg b/res/2.jpg new file mode 100644 index 0000000..c4e6d98 Binary files /dev/null and b/res/2.jpg differ diff --git a/res/3.jpg b/res/3.jpg new file mode 100644 index 0000000..e34b380 Binary files /dev/null and b/res/3.jpg differ diff --git a/res/4.jpg b/res/4.jpg new file mode 100644 index 0000000..fbbaaff Binary files /dev/null and b/res/4.jpg differ diff --git a/res/5.jpg b/res/5.jpg new file mode 100644 index 0000000..6537058 Binary files /dev/null and b/res/5.jpg differ diff --git a/res/6.jpg b/res/6.jpg new file mode 100644 index 0000000..05a3282 Binary files /dev/null and b/res/6.jpg differ diff --git a/res/7.jpg b/res/7.jpg new file mode 100644 index 0000000..7cacda4 Binary files /dev/null and b/res/7.jpg differ diff --git a/res/8.jpg b/res/8.jpg new file mode 100644 index 0000000..e34d500 Binary files /dev/null and b/res/8.jpg differ diff --git a/res/9.jpg b/res/9.jpg new file mode 100644 index 0000000..aecc770 Binary files /dev/null and b/res/9.jpg differ diff --git a/res/down.png b/res/down.png new file mode 100644 index 0000000..62770a0 Binary files /dev/null and b/res/down.png differ diff --git a/res/home.png b/res/home.png new file mode 100644 index 0000000..37a5efd Binary files /dev/null and b/res/home.png differ diff --git a/res/me.png b/res/me.png new file mode 100644 index 0000000..8182511 Binary files /dev/null and b/res/me.png differ diff --git a/res/post.jpg b/res/post.jpg new file mode 100644 index 0000000..9f5f565 Binary files /dev/null and b/res/post.jpg differ diff --git a/res/video.mp4 b/res/video.mp4 new file mode 100644 index 0000000..966f1b9 Binary files /dev/null and b/res/video.mp4 differ diff --git a/script.js b/script.js new file mode 100644 index 0000000..3063004 --- /dev/null +++ b/script.js @@ -0,0 +1,113 @@ +const initSlider = () => { + const initCarousel = (containerId, images) => { + const container = document.getElementById(containerId) + let imageList = container.querySelector('.image-list') + let maxScrollLeft + + const destroyCarousel = () => { + imageList.removeEventListener('scroll', handleScroll) + window.removeEventListener('resize', updateSlider) + imageList = null + maxScrollLeft = null + } + + const updateMaxScrollLeft = () => { + maxScrollLeft = imageList.scrollWidth - imageList.clientWidth + } + + const updateSlider = () => { + updateMaxScrollLeft() + updateScrollThumbPosition() + handleSlideButtons() + } + + const handleScroll = () => { + updateScrollThumbPosition() + handleSlideButtons() + } + + const updateScrollThumbPosition = () => { + const scrollPosition = imageList.scrollLeft + const thumbPosition = + (scrollPosition / maxScrollLeft) * + (sliderScrollbar.clientWidth - scrollbarThumb.offsetWidth) + scrollbarThumb.style.left = `${thumbPosition}px` + } + + const handleSlideButtons = () => { + slideButtons[0].style.display = imageList.scrollLeft <= 0 ? 'none' : 'flex' + slideButtons[1].style.display = + imageList.scrollLeft >= maxScrollLeft ? 'none' : 'flex' + } + + window.addEventListener('resize', updateSlider) + + images.forEach(imageSrc => { + const image = document.createElement('img') + image.src = imageSrc + image.classList.add('image-item') + imageList.appendChild(image) + }) + + const slideButtons = container.querySelectorAll('.slide-button') + const sliderScrollbar = container.querySelector('.slider-scrollbar') + const scrollbarThumb = sliderScrollbar.querySelector('.scrollbar-thumb') + + updateSlider() + + slideButtons.forEach((button) => { + button.addEventListener('click', () => { + const direction = button.id === 'prev-slide' ? -1 : 1 + const scrollAmount = imageList.clientWidth * direction + imageList.scrollBy({ left: scrollAmount, behavior: 'smooth' }) + }) + }) + + scrollbarThumb.addEventListener('mousedown', (e) => { + const startX = e.clientX + const thumbPosition = scrollbarThumb.offsetLeft + const maxThumbPosition = sliderScrollbar.clientWidth - scrollbarThumb.offsetWidth + + const handleMouseMove = (e) => { + const deltaX = e.clientX - startX + const newThumbPosition = thumbPosition + deltaX + const boundedPosition = Math.max( + 0, + Math.min(maxThumbPosition, newThumbPosition) + ) + const scrollPosition = + (boundedPosition / maxThumbPosition) * maxScrollLeft + + scrollbarThumb.style.left = `${boundedPosition}px` + imageList.scrollLeft = scrollPosition + } + + const handleMouseUp = () => { + document.removeEventListener('mousemove', handleMouseMove) + document.removeEventListener('mouseup', handleMouseUp) + } + + document.addEventListener('mousemove', handleMouseMove) + document.addEventListener('mouseup', handleMouseUp) + }) + + imageList.addEventListener('scroll', handleScroll) + + return destroyCarousel + } + + const slider1Images = ['res/1.jpg', 'res/2.jpg', 'res/3.jpg', 'res/4.jpg', 'res/5.jpg', 'res/1.jpg'] + const destroySlider1 = initCarousel('slider1', slider1Images) + + const slider2Images = ['res/6.jpg', 'res/7.jpg', 'res/8.jpg', 'res/9.jpg', 'res/10.jpg', 'res/9.jpg'] + const destroySlider2 = initCarousel('slider2', slider2Images) +} + +function scrollToBottom () { + window.scrollTo({ + top: document.body.scrollHeight, + behavior: 'smooth' + }) +} + +window.addEventListener('load', initSlider) diff --git a/style.css b/style.css new file mode 100644 index 0000000..b1d7250 --- /dev/null +++ b/style.css @@ -0,0 +1,177 @@ +/* stylelint-disable selector-class-pattern */ +*, +::before, +::after { + box-sizing: border-box; +} + +body { + margin: 0; + font-family: Montserrat, sans-serif; +} + +img { + width: 100%; + height: 100%; +} + +h1, +h2, +h3 { + margin: 0; +} + +.header { + width: 100%; + padding: 15px 0; + display: flex; + justify-content: center; + align-items: center; + font-size: 24px; + font-weight: 300; + color: gray; +} + +.hero { + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; +} + +.container { + max-width: 1230px; + width: 100%; + margin: 0 auto; + padding: 0 15px; +} + +.hero-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); + grid-gap: 20px; +} + +.hero-about { + display: flex; + flex-direction: column; + justify-content: center; +} + +.hero-title { + font-size: 48px; + line-height: 1.1; + margin-bottom: 20px; +} + +.hero-prof { + color: #4facfe; + white-space: nowrap; +} + +.hero-subtitle { + font-size: 32px; + font-weight: 400; +} + +.hero-links { + display: flex; +} + +.hero-description__link { + margin-top: 50px; + color: #000; +} + +.hero-description__link:hover { + color: #4facfe; +} + +.hero-box { + max-width: 500px; + justify-self: center; + overflow: hidden; + border: 10px solid #fff; + background-image: linear-gradient(to right, #4facfe 0%, #00f2fe 100%); + animation: ava-animate 5s infinite alternate; + box-shadow: 2px 5px 10px rgb(0 0 0 / 50%); +} + +.hero-box__img { + animation: ava-bg 3s infinite alternate; +} + +@keyframes fade { + from { + opacity: 0.4; + } + + to { + opacity: 1; + } +} + +@media screen and (width <= 978px) { + .hero { + padding-top: 30px; + } + + .hero-container { + grid-row-gap: 20px; + } + + .hero-title { + font-size: 28px; + margin-bottom: 10px; + } + + .hero-subtitle { + font-size: 22px; + font-weight: 400; + } + + .hero-description { + font-size: 14px; + } + + .hero-about { + text-align: center; + } + + .hero-links { + justify-content: center; + } + + .hero-box { + max-width: 350px; + justify-self: center; + } +} + +@keyframes ava-animate { + 0% { + border-radius: 62% 38% 72% 28% / 67% 32% 68% 33%; + } + + 40% { + border-radius: 37% 63% 53% 47% / 67% 32% 68% 33%; + } + + 60% { + border-radius: 23% 77% 39% 61% / 78% 54% 46% 22%; + } + + 100% { + border-radius: 18% 82% 24% 76% / 43% 54% 46% 57%; + } +} + +@keyframes ava-bg { + from { + backdrop-filter: hue-rotate(0); + } + + to { + backdrop-filter: hue-rotate(90deg); + } +}