Skip to content

Commit 2a94bcd

Browse files
authored
Merge pull request #16 from Alexandra0107/module9-task2
2 parents 200d65b + dbb7543 commit 2a94bcd

6 files changed

Lines changed: 271 additions & 4 deletions

File tree

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
<link rel="stylesheet" href="css/normalize.css">
77
<link rel="stylesheet" href="css/style.css">
88
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
9+
<link rel="stylesheet" href="./vendor/nouislider/nouislider.css">
910
<title>Кекстаграм</title>
1011
</head>
1112

@@ -237,5 +238,6 @@ <h2 class="data-error__title">Не удалось загрузить данны
237238

238239
<script type="module" src="./js/main.js"></script>
239240
<script src="./vendor/pristine/pristine.min.js"></script>
241+
<script src="./vendor/nouislider/nouislider.js"></script>
240242
</body>
241243
</html>

js/data.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,61 @@ const MESSAGES = [
2121
'Лица у людей на фотке перекошены, как будто их избивают. Как можно было поймать такой неудачный момент?!'
2222
];
2323

24-
export {NAMES, MESSAGES};
24+
// Константы для управления масштабом изображения
25+
const SCALE = {
26+
STEP: 25, // шаг изменения в процентах
27+
MIN_VALUE: 25, // минимальное значение в процентах
28+
MAX_VALUE: 100, // максимальное значение в процентах
29+
DEFAULT_VALUE: 100 // значение по умолчанию в процентах
30+
};
31+
32+
// Константы для эффектов
33+
const EFFECTS = {
34+
'none': {
35+
filter: null,
36+
min: 0,
37+
max: 100,
38+
step: 1,
39+
unit: ''
40+
},
41+
'chrome': {
42+
filter: 'grayscale',
43+
min: 0,
44+
max: 1,
45+
step: 0.1,
46+
unit: ''
47+
},
48+
'sepia': {
49+
filter: 'sepia',
50+
min: 0,
51+
max: 1,
52+
step: 0.1,
53+
unit: ''
54+
},
55+
'marvin': {
56+
filter: 'invert',
57+
min: 0,
58+
max: 100,
59+
step: 1,
60+
unit: '%'
61+
},
62+
'phobos': {
63+
filter: 'blur',
64+
min: 0,
65+
max: 3,
66+
step: 0.1,
67+
unit: 'px'
68+
},
69+
'heat': {
70+
filter: 'brightness',
71+
min: 1,
72+
max: 3,
73+
step: 0.1,
74+
unit: ''
75+
}
76+
};
77+
// Значение по умолчанию для эффекта
78+
const DEFAULT_EFFECT = 'none';
79+
80+
export {NAMES, MESSAGES,SCALE, EFFECTS, DEFAULT_EFFECT};
2581

js/dom.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ const getCancelButton = () => document.querySelector('.img-upload__cancel');
77
const getHashtagsInput = () => document.querySelector('.text__hashtags');
88
const getDescriptionInput = () => document.querySelector('.text__description');
99
const getUploadForm = () => document.querySelector('#upload-select-image');
10+
const getScaleSmallerButton = () => document.querySelector('.scale__control--smaller');
11+
const getScaleBiggerButton = () => document.querySelector('.scale__control--bigger');
12+
const getScaleValueField = () => document.querySelector('.scale__control--value');
13+
const getEffectsList = () => document.querySelector('.effects');
14+
const getEffectLevelSlider = () => document.querySelector('.effect-level__slider');
15+
const getEffectLevelValue = () => document.querySelector('.effect-level__value');
16+
const getEffectLevelContainer = () => document.querySelector('.img-upload__effect-level');
1017

1118
//получаем контейнер для списка комментариев
1219
const getCommentsContainer = () => {
@@ -48,4 +55,4 @@ const getImageElement = () => {
4855
const bigPicture = getBigPicture();
4956
return bigPicture?.querySelector('.big-picture__img img') || null;
5057
};
51-
export {getBigPicture,getBody, getFileInput,getOverlay,getPreviewImage,getCancelButton,getHashtagsInput,getDescriptionInput,getUploadForm,getCommentsContainer,getCommentCountElement,getCommentsLoaderElement,getShownCommentCountElement,getTotalCommentCountElement,getLikesCountElement,getCaptionElement,getImageElement};
58+
export {getBigPicture,getBody, getFileInput,getOverlay,getPreviewImage,getCancelButton,getHashtagsInput,getDescriptionInput,getUploadForm,getCommentsContainer,getCommentCountElement,getCommentsLoaderElement,getShownCommentCountElement,getTotalCommentCountElement,getLikesCountElement,getCaptionElement,getImageElement,getScaleSmallerButton,getScaleBiggerButton,getScaleValueField,getEffectsList,getEffectLevelSlider,getEffectLevelValue,getEffectLevelContainer};

js/full-screen-vewer.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,6 @@ const initGallery = () => {
185185
setupLoadMoreHandler();
186186
};
187187

188-
document.addEventListener('DOMContentLoaded', initGallery);
189188
const initThumbnailHandlers = (photoDataList) => {
190189
const picturesContainer = document.querySelector('.pictures');
191190

@@ -208,4 +207,6 @@ const initThumbnailHandlers = (photoDataList) => {
208207
});
209208
};
210209

210+
document.addEventListener('DOMContentLoaded', initGallery);
211+
211212
export {initGallery,initThumbnailHandlers};

js/image-processor.js

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import {
2+
getScaleSmallerButton,
3+
getScaleBiggerButton,
4+
getScaleValueField,
5+
getPreviewImage,
6+
getEffectsList,
7+
getEffectLevelSlider,
8+
getEffectLevelValue,
9+
getEffectLevelContainer
10+
} from './dom.js';
11+
import { SCALE, EFFECTS, DEFAULT_EFFECT } from './data.js';
12+
13+
// Получает текущий выбранный эффект
14+
const getCurrentEffect = () => {
15+
const effectsList = getEffectsList();
16+
if (!effectsList) {
17+
return DEFAULT_EFFECT;
18+
}
19+
20+
const activeEffect = effectsList.querySelector('.effects__radio:checked');
21+
return activeEffect ? activeEffect.value : DEFAULT_EFFECT;
22+
};
23+
24+
// Применяет масштабирование к изображению
25+
const applyScaleToImage = (scalePercent) => {
26+
const scaleValue = scalePercent / 100;
27+
const previewImage = getPreviewImage();
28+
if (previewImage) {
29+
previewImage.style.transform = `scale(${scaleValue})`;
30+
}
31+
};
32+
33+
// Обновляет масштаб (только кнопки и поле ввода)
34+
const updateScale = (newValue) => {
35+
// Ограничиваем значение диапазоном
36+
const clampedValue = Math.max(
37+
SCALE.MIN_VALUE,
38+
Math.min(SCALE.MAX_VALUE, newValue)
39+
);
40+
41+
// Устанавливаем новое значение с символом %
42+
const valueField = getScaleValueField();
43+
valueField.value = `${clampedValue}%`;
44+
45+
// Применяем трансформацию к изображению
46+
applyScaleToImage(clampedValue);
47+
};
48+
49+
// Применяет CSS‑фильтр к изображению
50+
const applyEffectToImage = (effect, value) => {
51+
const previewImage = getPreviewImage();
52+
if (!previewImage){
53+
return;
54+
}
55+
const effectConfig = EFFECTS[effect];
56+
if (effect === 'none' || !effectConfig.filter) {
57+
previewImage.style.filter = 'none';
58+
return;
59+
}
60+
61+
const unit = effectConfig.unit || '';
62+
previewImage.style.filter = `${effectConfig.filter}(${value}${unit})`;
63+
};
64+
65+
// Инициализация слайдера уровня эффекта
66+
const initEffectSlider = () => {
67+
const slider = getEffectLevelSlider();
68+
const container = getEffectLevelContainer();
69+
70+
if (!slider || !container) {
71+
// Отключаем правило для этой строки
72+
// eslint-disable-next-line no-console
73+
console.warn('Элементы слайдера эффекта не найдены');
74+
return;
75+
}
76+
77+
try {
78+
// Скрываем контейнер слайдера по умолчанию
79+
container.classList.add('hidden');
80+
81+
noUiSlider.create(slider, {
82+
start: [EFFECTS[DEFAULT_EFFECT].min],
83+
connect: 'lower',
84+
range: {
85+
'min': EFFECTS[DEFAULT_EFFECT].min,
86+
'max': EFFECTS[DEFAULT_EFFECT].max
87+
},
88+
step: EFFECTS[DEFAULT_EFFECT].step,
89+
format: {
90+
to: (value) => Math.round(value * 100) / 100, // округление до 2 знаков
91+
from: (value) => parseFloat(value)
92+
}
93+
});
94+
95+
// Обработчик изменения значения слайдера
96+
slider.noUiSlider.on('update', (values, handle) => {
97+
const currentEffect = getCurrentEffect();
98+
const effectConfig = EFFECTS[currentEffect];
99+
const value = Number(values[handle]);
100+
101+
// Обновляем отображение значения
102+
const valueDisplay = getEffectLevelValue();
103+
if (valueDisplay) {
104+
valueDisplay.textContent = `${value}${effectConfig.unit}`;
105+
}
106+
107+
// Применяем эффект
108+
applyEffectToImage(currentEffect, value);
109+
});
110+
} catch (error) {
111+
// Отключаем правило для этой строки
112+
// eslint-disable-next-line no-console
113+
console.error('Ошибка инициализации слайдера эффекта:', error);
114+
}
115+
};
116+
117+
// Обновляет слайдер при смене эффекта
118+
const updateEffectSlider = (effect) => {
119+
const slider = getEffectLevelSlider();
120+
const container = getEffectLevelContainer();
121+
const valueDisplay = getEffectLevelValue();
122+
123+
if (!slider || !slider.noUiSlider || !container || !valueDisplay) {
124+
return;
125+
}
126+
127+
const effectConfig = EFFECTS[effect];
128+
129+
if (effect === 'none') {
130+
// Скрываем слайдер для эффекта 'none'
131+
container.classList.add('hidden');
132+
getPreviewImage().style.filter = 'none';
133+
} else {
134+
// Обновляем настройки слайдера
135+
slider.noUiSlider.updateOptions({
136+
range: { min: effectConfig.min, max: effectConfig.max },
137+
step: effectConfig.step,
138+
start: [effectConfig.min]
139+
});
140+
141+
// Показываем контейнер и обновляем отображение
142+
container.classList.remove('hidden');
143+
valueDisplay.textContent = `${effectConfig.min}${effectConfig.unit}`;
144+
145+
// Применяем начальный эффект
146+
applyEffectToImage(effect, effectConfig.min);
147+
}
148+
};
149+
150+
// Обработчик смены эффекта
151+
const initEffectsControls = () => {
152+
const effectsList = getEffectsList();
153+
if (!effectsList) {
154+
return;
155+
}
156+
157+
effectsList.addEventListener('change', (e) => {
158+
if (e.target.matches('.effects__radio')) {
159+
updateEffectSlider(e.target.value);
160+
}
161+
});
162+
};
163+
164+
const initScaleControls = () => {
165+
const smallerButton = getScaleSmallerButton();
166+
const biggerButton = getScaleBiggerButton();
167+
const valueField = getScaleValueField();
168+
169+
// Устанавливаем значение по умолчанию для масштаба
170+
valueField.value = `${SCALE.DEFAULT_VALUE}%`;
171+
applyScaleToImage(SCALE.DEFAULT_VALUE);
172+
173+
// Обработчик для кнопки уменьшения масштаба
174+
if (smallerButton) {
175+
smallerButton.addEventListener('click', () => {
176+
const currentValue = parseInt(valueField.value, 10);
177+
updateScale(currentValue - SCALE.STEP);
178+
});
179+
}
180+
181+
// Обработчик для кнопки увеличения масштаба
182+
if (biggerButton) {
183+
biggerButton.addEventListener('click', () => {
184+
const currentValue = parseInt(valueField.value, 10);
185+
updateScale(currentValue + SCALE.STEP);
186+
});
187+
}
188+
189+
// Инициализируем слайдер эффектов
190+
initEffectSlider();
191+
192+
// Инициализируем управление эффектами
193+
initEffectsControls();
194+
195+
// Устанавливаем эффект по умолчанию
196+
updateEffectSlider(DEFAULT_EFFECT);
197+
};
198+
199+
export { initScaleControls };

js/main.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,18 @@ import { getThumbs } from './thumbs.js';
22
import { renderThumbs } from './render.js';
33
import { initGallery,initThumbnailHandlers} from './full-screen-vewer.js';
44
import { showEditForm, hideEditForm } from './image-upload.js';
5-
5+
import { initScaleControls } from './image-processor.js';
66
// Получаем данные
77
const thumbsList = getThumbs();
88

99
// получаем контейнер после отрисвки в render и вызываем функцию
1010
renderThumbs(thumbsList);
1111
showEditForm();
1212
hideEditForm();
13+
1314
document.addEventListener('DOMContentLoaded', () => {
1415
initGallery();
1516
initThumbnailHandlers(thumbsList);
17+
initScaleControls();
1618
});
1719

0 commit comments

Comments
 (0)