17 July, 2022

Xây dựng trình chỉnh sửa hình ảnh trong HTML CSS & JavaScript


Xin chào các bạn, hôm nay trong blog này, bạn sẽ học cách tạo một dự án mới có tên là Build An Basic Image Editor trong HTML CSS & JavaScript. Trước đây, tôi đã chia sẻ một blog về cách xây dựng trò chơi đoán từ trong JavaScript tương tự như treo cổ.

Trong dự án này (Trình chỉnh sửa hình ảnh trong JavaScript), như bạn đã thấy trong hình ảnh xem trước, người dùng có thể áp dụng các bộ lọc khác nhau cho hình ảnh của họ như thang độ xám, đảo ngược, độ bão hòa và điều chỉnh độ sáng của hình ảnh. Ngoài ra, người dùng cũng có thể xoay hoặc lật hình ảnh và lưu hình ảnh đã chỉnh sửa của họ.

Dự án này được thực hiện với JavaScript vani. Nếu bạn hào hứng muốn xem bản demo của trình chỉnh sửa hình ảnh này hoặc muốn biết trình chỉnh sửa này trông như thế nào. Bạn có thể xem video YouTube đã cho hoặc nhấp vào đây để xem bản demo trực tiếp của trình chỉnh sửa hình ảnh cơ bản này.


Trong video trên, bạn đã xem bản demo của Trình chỉnh sửa hình ảnh này và cách tôi tạo nó bằng HTML CSS & Vanilla JavaScript. Nếu bạn là người mới bắt đầu sử dụng JavaScript và chưa từng xây dựng các dự án trước đó, thì mã trình chỉnh sửa hình ảnh này có thể khó hiểu.

Vì vậy, tôi muốn đề xuất bạn xây dựng một số dự án JavaScript trước khi tiếp tục tạo trình chỉnh sửa hình ảnh này. Tuy nhiên, nếu bạn đã quen thuộc với JavaScript, thì các mã và logic của dự án này sẽ không khó hiểu đối với bạn.

Phần mã canvas có thể khó hiểu đối với bạn và canvas chỉ được sử dụng để lưu hình ảnh đã chỉnh sửa. Vì vậy, vui lòng xem video trên nhiều lần để hiểu các mã vì tôi đã cố gắng hết sức để giải thích các dòng JavaScript chính bằng các nhận xét bằng văn bản.

Nếu bạn thích trình chỉnh sửa hình ảnh này và muốn lấy mã nguồn và tệp, bạn có thể dễ dàng lấy chúng từ cuối trang này.

Trình chỉnh sửa hình ảnh cơ bản trong JavaScript [Mã nguồn]

Để xây dựng Trình chỉnh sửa hình ảnh bằng HTML CSS & JavaScript, bạn cần tạo ba tệp: HTML, CSS & JavaScript tệp. Khi bạn tạo các tệp này, chỉ cần dán các mã đã cho vào tệp của bạn. Nếu bạn không biết cách tạo các tệp này, nơi dán mã hoặc không muốn làm những việc này, bạn chỉ cần tải xuống tệp mã nguồn của Trình chỉnh sửa hình ảnh này bằng cách nhấp vào nút tải xuống đã cho ở cuối trang này.

Đầu tiên, tạo một tệp HTML với tên index.html và dán các mã đã cho vào tệp HTML của bạn. Hãy nhớ rằng, bạn phải tạo một tệp có phần mở rộng .html.
<!DOCTYPE html>
<!-- Coding By CodingNepal - youtube.com/codingnepal -->
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>Image Editor in JavaScript | CodingNepal</title>
    <link rel="stylesheet" href="style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/boxicons@2.1.2/css/boxicons.min.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/css/all.min.css"/>
  </head>
  <body>
    <div class="container disable">
        <h2>Easy Image Editor</h2>
        <div class="wrapper">
            <div class="editor-panel">
                <div class="filter">
                    <label class="title">Filters</label>
                    <div class="options">
                        <button id="brightness" class="active">Brightness</button>
                        <button id="saturation">Saturation</button>
                        <button id="inversion">Inversion</button>
                        <button id="grayscale">Grayscale</button>
                    </div>
                    <div class="slider">
                        <div class="filter-info">
                            <p class="name">Brighteness</p>
                            <p class="value">100%</p>
                        </div>
                        <input type="range" value="100" min="0" max="200">
                    </div>
                </div>
                <div class="rotate">
                    <label class="title">Rotate & Flip</label>
                    <div class="options">
                        <button id="left"><i class="fa-solid fa-rotate-left"></i></button>
                        <button id="right"><i class="fa-solid fa-rotate-right"></i></button>
                        <button id="horizontal"><i class='bx bx-reflect-vertical'></i></button>
                        <button id="vertical"><i class='bx bx-reflect-horizontal' ></i></button>
                    </div>
                </div>
            </div>
            <div class="preview-img">
                <img src="image-placeholder.svg" alt="preview-img">
            </div>
        </div>
        <div class="controls">
            <button class="reset-filter">Reset Filters</button>
            <div class="row">
                <input type="file" class="file-input" accept="image/*" hidden>
                <button class="choose-img">Choose Image</button>
                <button class="save-img">Save Image</button>
            </div>
        </div>
    </div>

    <script src="script.js"></script>
    
  </body>
</html>

Thứ hai, tạo một tệp CSS với tên style.css và dán các mã đã cho vào tệp CSS của bạn. Hãy nhớ rằng, bạn phải tạo một tệp có phần mở rộng .css.
/* Import Google font - Poppins */
@import url('https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap');
*{
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  font-family: 'Poppins', sans-serif;
}
body{
  display: flex;
  padding: 10px;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
  background: #E3F2FD;
}
.container{
  width: 850px;
  padding: 30px 35px 35px;
  background: #fff;
  border-radius: 10px;
  box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.container.disable .editor-panel,
.container.disable .controls .reset-filter,
.container.disable .controls .save-img{
  opacity: 0.5;
  pointer-events: none;
}
.container h2{
  margin-top: -8px;
  font-size: 22px;
  font-weight: 500;
}
.container .wrapper{
  display: flex;
  margin: 20px 0;
  min-height: 335px;
}
.wrapper .editor-panel{
  padding: 15px 20px;
  width: 280px;
  border-radius: 5px;
  border: 1px solid #ccc;
}
.editor-panel .title{
  display: block;
  font-size: 16px;
  margin-bottom: 12px;
}
.editor-panel .options, .controls{
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
.editor-panel button{
  outline: none;
  height: 40px;
  font-size: 14px;
  color: #6C757D;
  background: #fff;
  border-radius: 3px;
  margin-bottom: 8px;
  border: 1px solid #aaa;
}
.editor-panel .filter button{
  width: calc(100% / 2 - 4px);
}
.editor-panel button:hover{
  background: #f5f5f5;
}
.filter button.active{
  color: #fff;
  border-color: #5372F0;
  background: #5372F0;
}
.filter .slider{
  margin-top: 12px;
}
.filter .slider .filter-info{
  display: flex;
  color: #464646;
  font-size: 14px;
  justify-content: space-between;
}
.filter .slider input{
  width: 100%;
  height: 5px;
  accent-color: #5372F0;
}
.editor-panel .rotate{
  margin-top: 17px;
}
.editor-panel .rotate button{
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(100% / 4 - 3px);
}
.rotate .options button:nth-child(3),
.rotate .options button:nth-child(4){
  font-size: 18px;
}
.rotate .options button:active{
  color: #fff;
  background: #5372F0;
  border-color: #5372F0;
}
.wrapper .preview-img{
  flex-grow: 1;
  display: flex;
  overflow: hidden;
  margin-left: 20px;
  border-radius: 5px;
  align-items: center;
  justify-content: center;
}
.preview-img img{
  max-width: 490px;
  max-height: 335px;
  width: 100%;
  height: 100%;
  object-fit: contain;
}
.controls button{
  padding: 11px 20px;
  font-size: 14px;
  border-radius: 3px;
  outline: none;
  color: #fff;
  cursor: pointer;
  background: none;
  transition: all 0.3s ease;
  text-transform: uppercase;
}
.controls .reset-filter{
  color: #6C757D;
  border: 1px solid #6C757D;
}
.controls .reset-filter:hover{
  color: #fff;
  background: #6C757D;
}
.controls .choose-img{
  background: #6C757D;
  border: 1px solid #6C757D;
}
.controls .save-img{
  margin-left: 5px;
  background: #5372F0;
  border: 1px solid #5372F0;
}

@media screen and (max-width: 760px) {
  .container{
    padding: 25px;
  }
  .container .wrapper{
    flex-wrap: wrap-reverse;
  }
  .wrapper .editor-panel{
    width: 100%;
  }
  .wrapper .preview-img{
    width: 100%;
    margin: 0 0 15px;
  }
}

@media screen and (max-width: 500px) {
  .controls button{
    width: 100%;
    margin-bottom: 10px;
  }
  .controls .row{
    width: 100%;
  }
  .controls .row .save-img{
    margin-left: 0px;
  }
}

Cuối cùng, tạo một tệp JavaScript với tên script.js và dán các mã đã cho vào tệp JavaScript của bạn. Hãy nhớ rằng, bạn phải tạo một tệp có phần mở rộng .js.
const fileInput = document.querySelector(".file-input"),
filterOptions = document.querySelectorAll(".filter button"),
filterName = document.querySelector(".filter-info .name"),
filterValue = document.querySelector(".filter-info .value"),
filterSlider = document.querySelector(".slider input"),
rotateOptions = document.querySelectorAll(".rotate button"),
previewImg = document.querySelector(".preview-img img"),
resetFilterBtn = document.querySelector(".reset-filter"),
chooseImgBtn = document.querySelector(".choose-img"),
saveImgBtn = document.querySelector(".save-img");

let brightness = "100", saturation = "100", inversion = "0", grayscale = "0";
let rotate = 0, flipHorizontal = 1, flipVertical = 1;

const loadImage = () => {
    let file = fileInput.files[0];
    if(!file) return;
    previewImg.src = URL.createObjectURL(file);
    previewImg.addEventListener("load", () => {
        resetFilterBtn.click();
        document.querySelector(".container").classList.remove("disable");
    });
}

const applyFilter = () => {
    previewImg.style.transform = `rotate(${rotate}deg) scale(${flipHorizontal}, ${flipVertical})`;
    previewImg.style.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`;
}

filterOptions.forEach(option => {
    option.addEventListener("click", () => {
        document.querySelector(".active").classList.remove("active");
        option.classList.add("active");
        filterName.innerText = option.innerText;

        if(option.id === "brightness") {
            filterSlider.max = "200";
            filterSlider.value = brightness;
            filterValue.innerText = `${brightness}%`;
        } else if(option.id === "saturation") {
            filterSlider.max = "200";
            filterSlider.value = saturation;
            filterValue.innerText = `${saturation}%`
        } else if(option.id === "inversion") {
            filterSlider.max = "100";
            filterSlider.value = inversion;
            filterValue.innerText = `${inversion}%`;
        } else {
            filterSlider.max = "100";
            filterSlider.value = grayscale;
            filterValue.innerText = `${grayscale}%`;
        }
    });
});

const updateFilter = () => {
    filterValue.innerText = `${filterSlider.value}%`;
    const selectedFilter = document.querySelector(".filter .active");

    if(selectedFilter.id === "brightness") {
        brightness = filterSlider.value;
    } else if(selectedFilter.id === "saturation") {
        saturation = filterSlider.value;
    } else if(selectedFilter.id === "inversion") {
        inversion = filterSlider.value;
    } else {
        grayscale = filterSlider.value;
    }
    applyFilter();
}

rotateOptions.forEach(option => {
    option.addEventListener("click", () => {
        if(option.id === "left") {
            rotate -= 90;
        } else if(option.id === "right") {
            rotate += 90;
        } else if(option.id === "horizontal") {
            flipHorizontal = flipHorizontal === 1 ? -1 : 1;
        } else {
            flipVertical = flipVertical === 1 ? -1 : 1;
        }
        applyFilter();
    });
});

const resetFilter = () => {
    brightness = "100"; saturation = "100"; inversion = "0"; grayscale = "0";
    rotate = 0; flipHorizontal = 1; flipVertical = 1;
    filterOptions[0].click();
    applyFilter();
}

const saveImage = () => {
    const canvas = document.createElement("canvas");
    const ctx = canvas.getContext("2d");
    canvas.width = previewImg.naturalWidth;
    canvas.height = previewImg.naturalHeight;
    
    ctx.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`;
    ctx.translate(canvas.width / 2, canvas.height / 2);
    if(rotate !== 0) {
        ctx.rotate(rotate * Math.PI / 180);
    }
    ctx.scale(flipHorizontal, flipVertical);
    ctx.drawImage(previewImg, -canvas.width / 2, -canvas.height / 2, canvas.width, canvas.height);
    
    const link = document.createElement("a");
    link.download = "image.jpg";
    link.href = canvas.toDataURL();
    link.click();
}

filterSlider.addEventListener("input", updateFilter);
resetFilterBtn.addEventListener("click", resetFilter);
saveImgBtn.addEventListener("click", saveImage);
fileInput.addEventListener("change", loadImage);
chooseImgBtn.addEventListener("click", () => fileInput.click());

Đó là tất cả, bây giờ bạn đã xây dựng thành công Trình chỉnh sửa hình ảnh cơ bản trong HTML CSS & JavaScript. Nếu mã của bạn không hoạt động hoặc bạn gặp phải bất kỳ sự cố nào, vui lòng tải xuống các tệp mã nguồn từ nút tải xuống nhất định. Nó miễn phí và một tệp zip sẽ được tải xuống có chứa thư mục dự án với các tệp mã nguồn.




Nguồn : Codingnepalweb
All Right Reserved © 2015 By Hung Pro VN
Hung.Pro.VN Sharing Your Own Knowledge and Creative Thinking Every Day and Many Other Things.