Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Добавляет раздел "На практике", исправляет демку и формулировку в доке про perspective #5437

Merged
merged 9 commits into from
Jul 19, 2024
176 changes: 176 additions & 0 deletions css/perspective/demos/dynamic-practice/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<title>Интерактивная песочница — perspective — Дока</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto&display=swap">
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&display=swap">
<style>
*, *::before, *::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}

html {
color-scheme: dark;
accent-color: #2E9AFF;
}

body {
min-height: 100vh;
padding: 50px;
display: flex;
align-items: center;
justify-content: center;
background-color: #18191C;
color: #FFFFFF;
font-family: "Roboto", sans-serif;
font-size: 18px;
overflow: hidden;
}

code {
font-family: "Roboto Mono", monospace;
font-size: calc(1em - 1px);
}

.container {
width: 500px;
margin: 0 auto;
}

.controls {
margin-bottom: 25px;
text-align: center;
text-transform: lowercase;
}

.value {
font-size: 40px;
font-weight: 500;
}

.slider {
display: block;
width: 100%;
margin: 5px 0;
}

.plane {
width: 500px;
height: 250px;
perspective: 500px;
transform-style: preserve-3d;
background-color: #2e9aff80;
display: grid;
grid-template-rows: 40px 1fr 40px;
justify-items: center;
align-items: center;
}

.element {
text-align: left;
width: 500px;
height: 150px;
background-color: #F498AD;
transform: rotateY(45deg);
position: relative;
}

.line {
width: 1px;
height: calc(100% - 2px);
position: absolute;
top: 0;
left: -10px;
background-color: #fff;
}

.line::before,
.line::after {
content: "";
position: absolute;
top: 0;
height: 1px;
left: -4px;
width: 10px;
background-color: #fff;
}

.line::after {
top: 100%;
}

.height-calc {
transform: rotateZ(-90deg);
position: absolute;
left: -35px;
top: 44%;
}

@media (max-width: 768px) {
body {
padding: 30px;
}

.container, .plane, .element {
width: 300px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="controls">
<label>
<code class="value"><span id="demo"></span></code>
<input type="range" min="200" max="1000" value="500" class="slider" id="myRange">
</label>
<p>Значение свойства <code>perspective</code></p>
</div>
<div class="plane">
Плоскость экрана
<div class="element">
<pre>
<code>
.pink-element {
width: 500px;
height: 150px;
transform: rotateY(45deg);
}
</code>
</pre>
<div class="line">
<code class="height-calc"></code>
</div>
</div>
</div>
</div>

<script>
const myRange = document.getElementById('myRange')
const output = document.getElementById('demo')
const plane = document.querySelector('.plane')
const element = document.querySelector('.element')
const heightCalc = document.querySelector('.height-calc')
const height = element.clientHeight
output.textContent = myRange.value + 'px'
heightCalc.textContent = "232px"

myRange.onchange = myRange.oninput = function () {
let value = myRange.value

plane.style.perspective = `${+value}px`
output.textContent = `${+value}px`

let multiplier = value / (value - (Math.sin(Math.PI / 4) * 250))

heightCalc.textContent = Math.round(height * multiplier) + "px"
}
</script>
</body>
</html>
56 changes: 18 additions & 38 deletions css/perspective/demos/dynamic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,45 +59,33 @@
margin: 5px 0;
}

.scene {
position: absolute;
.plane {
width: 500px;
height: 250px;
perspective: 500px;
}

.scene-wrapper {
width: 500px;
height: 250px;
transform-style: preserve-3d;
background-color: #2e9aff80;
display: grid;
grid-template-rows: 40px 1fr 40px;
justify-items: center;
align-items: center;
}

.element {
display: flex;
justify-content: center;
text-align: center;
width: 500px;
height: 250px;
height: 150px;
padding: 15px;
}

.element--front {
align-items: end;
background-color: #2e9aff80;
transform: rotateY(0) translateZ(100px);
}

.element--back {
align-items: start;
background-color: #F498AD;
transform: rotateY(180deg) translateZ(100px);
transform: rotateY(45deg);
}

@media (max-width: 768px) {
body {
padding: 30px;
}

.container, .scene, .scene-wrapper, .element {
.container, .plane, .element {
width: 300px;
}
}
Expand All @@ -108,36 +96,28 @@
<div class="controls">
<label>
<code class="value"><span id="demo"></span></code>
<input type="range" min="100" max="1000" value="500" class="slider" id="myRange">
<input type="range" min="200" max="1000" value="500" class="slider" id="myRange">
</label>
<p>Значение свойства <code>perspective</code></p>
</div>
<div class="scene">
<div class="scene-wrapper">
<div class="element element--back">Задняя стенка экрана</div>
</div>
<div class="plane">
Плоскость экрана
<div class="element">Элемент с <code>transform: rotateY(45deg)</code></div>
</div>
<div class="element element--front">Пользователь</div>
</div>

<script>
const myRange = document.getElementById('myRange')
const output = document.getElementById('demo')
const scene = document.querySelector('.scene')
const elementBack = document.querySelector('.element--back')
const plane = document.querySelector('.plane')
const element = document.querySelector('.element')
output.textContent = myRange.value + 'px'

myRange.onchange = myRange.oninput = function() {
let value = myRange.value
elementBack.style.transform='rotateY(180deg) translateZ(100px)'

if (value === '1000') {
value = 'none'
elementBack.style.transform='rotateY(180deg) translateZ(0)'
}

scene.style.perspective = value === 'none' ? 'none' : `${ +value}px`
output.textContent = value === 'none' ? 'none' : `${ +value}px`
plane.style.perspective = `${ +value}px`
output.textContent = `${ +value}px`
}
</script>
</body>
Expand Down
4 changes: 3 additions & 1 deletion css/perspective/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ title: "`perspective`"
description: "Перспектива есть всегда, даже в двумерном вебе."
authors:
- sqlzzy
contributors:
- denis-gh
related:
- css/transform
- css/transition
Expand All @@ -13,7 +15,7 @@ tags:

## Кратко

Свойство `perspective` определяет расстояние от пользователя до _задней стенки_ экрана по оси _z_. Таким образом можно придать глубину элементу, к которому применяется свойство [`transform`](/css/transform/). Эффект заметен только при 3D-трансформациях.
Свойство `perspective` определяет расстояние от пользователя до плоскости экрана по оси _z_. При уменьшении значения свойства `perspective` элементы (или части элементов), расположенные выше плоскости экрана по оси _z_ (ближе к зрителю) увеличиваются в размерах, а те, что дальше — уменьшаются. Таким образом можно придать глубину элементу, к которому применяется свойство [`transform`](/css/transform/). Эффект заметен только при 3D-трансформациях.

Свойство `perspective` влияет на _вложенные элементы_ контейнера, для которого указано, а не на сам контейнер.

Expand Down
5 changes: 5 additions & 0 deletions css/perspective/practice/denis-gh.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
🛠 Для точного масштабирования в перспективе мы можем определить, во сколько раз увеличится элемент, по формуле _d/(d-z)_, где _d_ — значение свойства `perspective`, _z_ — смещение элемента отностительно плоскости экрана по оси _z_. Смещение по оси _z_ определяем следующим образом: если задали элементу `transform: translateZ(100px)`, то параметр _z_ равен 100px, а если задали элементу `transform: rotateY(20deg)`, то для крайней левой точки элемента смещение по оси _z_ определяется как _sin(20)_, умноженный на половину изначальной ширины элемента.

Рассмотрим на конкретном примере: изначальная ширина элемента — 500px, высота — 150px, элемент повёрнут на 45°, родителю зададим `perspective: 700px`. Применяем формулу: 700 / (700 - sin(45) * 250) = 1,3379 — во столько раз увеличится высота элемента в крайней левой точке. Подставляя разные значения в формулу можем «на бумаге» рассчитать эффект перспективы.

<iframe title="Интерактивная песочница" src="../demos/dynamic-practice/" height="500"></iframe>
8 changes: 8 additions & 0 deletions people/denis-gh/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
name: 'Денис Вакуленко'
url: https://github.com/Denis-GH
badges:
- first-contribution-small
---

Начинающий фронтендер
Loading