Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="css/normalize.css">
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="vendor/nouislider/nouislider.css">
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
<title>Кекстаграм</title>
</head>
Expand Down Expand Up @@ -233,8 +234,9 @@ <h2 class="success__title">Изображение успешно загруже
<h2 class="data-error__title">Не удалось загрузить данные</h2>
</section>
</template>
<script src="vendor/nouislider/nouislider.js"></script>
<script src="vendor/pristine/pristine.min.js"></script>
<script src="js/main.js" type="module"></script>
<script src="js/functions.js"></script>
<!-- <script src="js/functions.js"></script> -->
</body>
</html>
6 changes: 5 additions & 1 deletion js/check-hashtag-validity.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ const hasValidHashtagLength = (value) =>
normalizeHashtags(value).every((hashtag) => hashtag.length <= MAX_HASHTAG_LENGTH);

const hasValidHashtagFormat = (value) =>
normalizeHashtags(value).every((hashtag) => HASHTAG_REGEXP.test(hashtag));
normalizeHashtags(value).every((hashtag) =>
hashtag.startsWith('#') &&
hashtag.length > 1 &&
HASHTAG_REGEXP.test(hashtag)
);

const hasUniqueHashtags = (value) => {
const hashtags = normalizeHashtags(value);
Expand Down
171 changes: 171 additions & 0 deletions js/image-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
const SCALE_STEP = 25;
const MIN_SCALE = 25;
const MAX_SCALE = 100;
const DEFAULT_SCALE = 100;
const DEFAULT_EFFECT = 'none';

const effectsConfig = {
none: {
range: { min: 0, max: 100 },
start: 100,
step: 1,
filter: '',
unit: ''
},
chrome: {
range: { min: 0, max: 1 },
start: 1,
step: 0.1,
filter: 'grayscale',
unit: ''
},
sepia: {
range: { min: 0, max: 1 },
start: 1,
step: 0.1,
filter: 'sepia',
unit: ''
},
marvin: {
range: { min: 0, max: 100 },
start: 100,
step: 1,
filter: 'invert',
unit: '%'
},
phobos: {
range: { min: 0, max: 3 },
start: 3,
step: 0.1,
filter: 'blur',
unit: 'px'
},
heat: {
range: { min: 1, max: 3 },
start: 3,
step: 0.1,
filter: 'brightness',
unit: ''
}
};

const uploadFormNode = document.querySelector('.img-upload__form');
const previewImageNode = uploadFormNode.querySelector('.img-upload__preview img');

const scaleValueNode = uploadFormNode.querySelector('.scale__control--value');
const scaleSmallerButtonNode = uploadFormNode.querySelector('.scale__control--smaller');
const scaleBiggerButtonNode = uploadFormNode.querySelector('.scale__control--bigger');

const effectsListNode = uploadFormNode.querySelector('.effects__list');
const effectLevelNode = uploadFormNode.querySelector('.img-upload__effect-level');
const effectLevelSliderNode = uploadFormNode.querySelector('.effect-level__slider');
const effectLevelValueNode = uploadFormNode.querySelector('.effect-level__value');

const hideEffectSlider = () => {
effectLevelNode.classList.add('hidden');
};

const showEffectSlider = () => {
effectLevelNode.classList.remove('hidden');
};

const applyScale = (scaleValue) => {
scaleValueNode.value = `${scaleValue}%`;
previewImageNode.style.transform = `scale(${scaleValue / 100})`;
};

const getScaleValue = () => parseInt(scaleValueNode.value, 10);

const getSelectedEffect = () =>
uploadFormNode.querySelector('.effects__radio:checked').value;

const applyEffect = (effectName, effectValue) => {
const currentEffect = effectsConfig[effectName];

if (effectName === DEFAULT_EFFECT) {
previewImageNode.style.filter = '';
effectLevelValueNode.value = '';
hideEffectSlider();
return;
}

previewImageNode.style.filter = `${currentEffect.filter}(${effectValue}${currentEffect.unit})`;
effectLevelValueNode.value = effectValue;
showEffectSlider();
};

const updateSliderOptions = (effectName) => {
const currentEffect = effectsConfig[effectName];

effectLevelSliderNode.noUiSlider.updateOptions({
range: currentEffect.range,
start: currentEffect.start,
step: currentEffect.step
});

effectLevelSliderNode.noUiSlider.set(currentEffect.start);
};

const onScaleSmallerButtonClick = () => {
const newScaleValue = Math.max(getScaleValue() - SCALE_STEP, MIN_SCALE);
applyScale(newScaleValue);
};

const onScaleBiggerButtonClick = () => {
const newScaleValue = Math.min(getScaleValue() + SCALE_STEP, MAX_SCALE);
applyScale(newScaleValue);
};

const onEffectsListChange = (evt) => {
if (!evt.target.matches('.effects__radio')) {
return;
}

updateSliderOptions(evt.target.value);
};

const onEffectLevelSliderUpdate = () => {
const selectedEffectName = getSelectedEffect();
const sliderValue = Number(effectLevelSliderNode.noUiSlider.get());

applyEffect(selectedEffectName, sliderValue);
};

const resetImageEditor = () => {
applyScale(DEFAULT_SCALE);
uploadFormNode.querySelector('#effect-none').checked = true;
previewImageNode.style.filter = '';
effectLevelValueNode.value = '';
hideEffectSlider();

effectLevelSliderNode.noUiSlider.updateOptions({
range: effectsConfig[DEFAULT_EFFECT].range,
start: effectsConfig[DEFAULT_EFFECT].start,
step: effectsConfig[DEFAULT_EFFECT].step
});

effectLevelSliderNode.noUiSlider.set(effectsConfig[DEFAULT_EFFECT].start);
};

const initImageEditor = () => {
applyScale(DEFAULT_SCALE);

if (!effectLevelSliderNode.noUiSlider) {
noUiSlider.create(effectLevelSliderNode, {
range: effectsConfig[DEFAULT_EFFECT].range,
start: effectsConfig[DEFAULT_EFFECT].start,
step: effectsConfig[DEFAULT_EFFECT].step,
connect: 'lower'
});

effectLevelSliderNode.noUiSlider.on('update', onEffectLevelSliderUpdate);
}

hideEffectSlider();

scaleSmallerButtonNode.addEventListener('click', onScaleSmallerButtonClick);
scaleBiggerButtonNode.addEventListener('click', onScaleBiggerButtonClick);
effectsListNode.addEventListener('change', onEffectsListChange);
};

export { initImageEditor, resetImageEditor };
2 changes: 2 additions & 0 deletions js/main.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { photos } from './create-array-photos.js';
import { renderThumbnails } from './render-thumbnails.js';
import { initUploadPhotoForm } from './upload-photo-form.js';
import { initImageEditor } from './image-editor.js';

renderThumbnails(photos);
initUploadPhotoForm();
initImageEditor();

15 changes: 9 additions & 6 deletions js/upload-photo-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
hasUniqueHashtags
} from './check-hashtag-validity.js';

import { resetImageEditor } from './image-editor.js';

const MAX_COMMENT_LENGTH = 140;

const bodyNode = document.body;
Expand All @@ -30,6 +32,7 @@ const resetUploadPhotoForm = () => {
uploadFormNode.reset();
uploadFileControl.value = '';
pristine.reset();
resetImageEditor();
};

const stopEscapePropagation = (evt) => {
Expand Down Expand Up @@ -78,20 +81,20 @@ const onUploadFormSubmit = (evt) => {

pristine.addValidator(
hashtagsInputNode,
hasValidHashtagCount,
'Нельзя указать больше пяти хэштегов'
hasValidHashtagFormat,
'Хэштег должен начинаться с # и содержать только буквы и цифры'
);

pristine.addValidator(
hashtagsInputNode,
hasValidHashtagLength,
'Максимальная длина одного хэштега — 20 символов'
hasValidHashtagCount,
'Нельзя указать больше пяти хэштегов'
);

pristine.addValidator(
hashtagsInputNode,
hasValidHashtagFormat,
'Хэштег должен начинаться с # и содержать только буквы и цифры'
hasValidHashtagLength,
'Максимальная длина одного хэштега — 20 символов'
);

pristine.addValidator(
Expand Down
Loading