hi - blog

如何使用 HTML + CSS + JAVASCRIPT 制作简易图片编辑器

如何使用 HTML + CSS + JAVASCRIPT 制作简易图片编辑器

图片编辑器

先上体验地址: https://weihuijieonline.com/project/image_editor

前言

最近在搞上传图片功能,偶然想到做一个图片编辑器,虽然两者也没啥关系,本文只介绍功能点实现,不会具体讲到样式信息,目前图片编辑器的功能:

  • 选择图片
  • 图片样式(灰度、饱和度、亮度、反转)
  • 图片方向(旋转、翻转)
  • 重置图片
  • 保存图片

准备工作

这个项目属于纯前端项目,这里就直接创建 index.htmlindex.cssindex.js 三个文件

目录

image_editor
|_ index.html
|_ index.css
|_ index.js

功能点实现

选择图片

// index.js
const fileInput = document.querySelector('.file-input'), // type='file' 选择文件的input
    chooseImgBtn = document.querySelector('.choose-img'), // 选择图片的按钮
    previewImg = document.querySelector('.preview-img img') // 放置图片容器

// 选择图片的回调方法
const loadImage = () => {
    let file = fileInput.files[0] // 获取用户选择的图片
    if (!file) return // 如果没有选择,return 出去
    // 有值赋给 图片容器
    previewImg.src = URL.createObjectURL(file)
}

// 上传功能
fileInput.addEventListener('change', loadImage)
chooseImgBtn.addEventListener('click', () => {
    // 点击选择图片按钮时,触发 input 方法
    fileInput.click()
})

改变图片样式

// index.js
const filterOptions = document.querySelectorAll('.filter button'), // filters 按钮组
    filterName = document.querySelector('.filter-info .name'), // 当前选择按钮的文字
    filterValue = document.querySelector('.filter-info .value'), // 当前图片该样式样式值
    filterSlider = document.querySelector('.slider input') // 图片样式的滑块
    ...

// 图片的默认值
let brightness = 100,
    saturation = 100,
    inversion = 0,
    grayscale = 0

// 滑块发生变化时的回调
const applyFilter = () => {
    // 保持图片持久化,不会因为更改其他值时样式重置
    previewImg.style.filter = `brightness(${brightness}%) saturate(${saturation}%) invert(${inversion}%) grayscale(${grayscale}%)`
}

...
filterOptions.forEach((option) => {
    // filterOptions => []
    // 给 filter buttons 添加点击事件
    option.addEventListener('click', () => {
        // 去除高亮的class
        document.querySelector('.filter .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 if (option.id === 'grayscale') {
            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 if (selectedFilter.id === 'grayscale') {
        grayscale = filterSlider.value
    }
    applyFilter()
}

filterSlider.addEventListener('input', updateFilter) // 滑块input事件
...

改变图片方向

...
const rotateOptions = document.querySelectorAll('.rotate button') // 旋转和翻转的按钮组

// 当前图片旋转和翻转的值
let rotate = 0,
    filpHorizontal = 1,
    filpVertical = 1


const applyFilter = () => {
    // 保持当前翻转和旋转值
    previewImg.style.transform = `rotate(${rotate}deg) scale(${filpHorizontal}, ${filpVertical})`
    ...
}

rotateOptions.forEach((option) => {
    option.addEventListener('click', () => {
        if (option.id === 'left') {
            rotate -= 90
        } else if (option.id === 'right') {
            rotate += 90
        } else if (option.id === 'vertical') {
            filpHorizontal = filpHorizontal === 1 ? -1 : 1
        } else if (option.id === 'horizontal') {
            filpVertical = filpVertical === 1 ? -1 : 1
        }
        applyFilter()
    })
})
...

重置图片

const resetFilterBtn = document.querySelector('.reset-filter') // 重置按钮
...

// 重置按钮回调 全部调为默认值
const resetFilter = () => {
    brightness = 100
    saturation = 100
    inversion = 0
    grayscale = 0
    rotate = 0
    filpHorizontal = 1
    filpVertical = 1
    filterOptions[0].click()
    applyFilter()
}

// 重置按钮点击监听
resetFilterBtn.addEventListener('click', resetFilter)
...

保存图片

const saveImgBtn = document.querySelector('.save-img') // 保存图片的容器
...

// 保存图片的回调
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) {
        ctx.rotate((rotate * Math.PI) / 180)
    }
    ctx.scale(filpHorizontal, filpVertical)
    // 将图片画出来
    ctx.drawImage(
        previewImg,
        -canvas.width / 2,
        -canvas.height / 2,
        canvas.width,
        canvas.height
    )
    // 创建一个 a 标签
    const link = document.createElement('a')
    // 给 a 下载内容做一个名称
    link.download = 'image.jpeg'
    // 通过画布给定一个 base64 的图片编码
    link.href = canvas.toDataURL()
    // 通过点击下载
    link.click()
    // 移除 a 标签
    document.removeChild(link)
}

// 保存图片按钮点击监听
saveImgBtn.addEventListener('click', saveImage)
...

最后一问

这个项目完成了么? 并没有

...
const loadImage = () => {
    ...
    previewImg.addEventListener('load', () => {
        resetFilterBtn.click()
	document
        .querySelector('.container')
        .classList
        .remove('disable')
	})
}
...

通过以上代码可以看出来,需要再做一些操作,从新选择图片的时候会保留上一个图片的数据,所以需要重置一下,下面这个去除的 class 是因为,这个图片编辑器如果不上传图片除选择图片按钮之外都是不可以点击的。

全是干货

结语

以上的代码为这个demo中全部功能点,全部代码 image_editor 记得点个 🌟哦

Current profile photo
© 2022 hi - blog
京ICP备2022015573号-1