File API文件操作之FileReader二

上一篇说了FileAPI中FileReader的readAsText,这里继续上文,说说另外一个API readAsDataURL。

这个接口是将File或者Blob读成base64格式的字符串,然后直接挂在HTML5的元素上,例如img就可以直接使用。

实际用途可以是图片预览和图片剪裁,这里我将用来实现图片剪裁。

思路:

1. file api的FileReader实现本地图片预览  

2. 用web api的拖拽功能来实现剪裁

 

效果:

File API文件操作之FileReader二

  

那么话不多说:

html代码:

<html>

<head>
    <title>FileReader 之 readAsDataURL</title>
</head>

<body style="margin: 2em 4em"  draggable="false">
    <div>图片剪裁</div>
    <input type="file" id="fileImageCut" value="选择图片"><br/>
    <div  draggable="false" id="imgWrapper" id="container1" style="display: inline-block;width: 500px">
        <div style="width: 404px;height: 404px;border:1px solid cornflowerblue;position: relative">
            <img draggable="false" style="position: absolute;left:-1px;bottom:-1px; border:1px solid greenyellow " id="imgPreview" />
            <div draggable="true" id="cutter" style="position: absolute;cursor:crosshair;left:-px;bottom:-2px; width:300px; height: 300px; border: 2px dotted sienna"></div>
        </div>
    </div>
    <div style="display: inline-block;width: 200px;height:200px;vertical-align:buttom;border: 1px solid cadetblue;overflow: hidden;position:relative">
        <img  draggable="false" style="width:266.7px;height:266.7px;position: absolute;left: 0;bottom: 0" id="imgResult">
    </div>
    <!--<div draggable="true" id="cutter" style="position: absolute;cursor:crosshair;left:62px;top:274px; width:200px; height: 200px; border: 2px dotted sienna"></div> -->
</body>
<script src="./js/readAsDataURL.js"></script>

</html>

 js代码:

const IMAGE_MAX_SIZE = 2
const CUTTER_WIDTH = CUTTER_HEIGHT = 300

class SimpleImageCutter {
    constructor(options) {
        this.fileUpload = options.fileUpload
        this.imgPreview = options.imgPreview
        this.imgResult = options.imgResult
        this.cutter = options.cutter

        this.percentage = this.imgResult.parentElement.clientWidth / this.cutter.clientWidth
        this.iLeft = this.iRight = this.iTop = this.iBottom = null
    }

    init() {
        this.resgiterEvents()
    }

    resgiterEvents() {
        let cLeft, cRight, cTop, cBottom, cOffsetX, cOffsetY,
            cutter = this.cutter, imgPreview = this.imgPreview, cBorderWidth = Number.parseInt(cutter.style.borderWidth.replace('px', '')),
            cPBorderWidth = Number.parseInt(imgPreview.style.borderWidth.replace('px', ''))

        this.fileUpload.addEventListener('change', (ev) => {
            let files = ev.target.files, file;
            //检查图片类型
            if (files.length && (file = files[0])) {
                if (!this.checkFile(file)) {
                    return
                }
                //重置高宽
                imgPreview.removeAttribute('height')
                imgPreview.removeAttribute('width')
                imgPreview.style.width = imgPreview.style.height = null
                imgPreview.style.visibility = 'hidden'
                let fr = new FileReader()
                fr.onload = () => {
                    imgPreview.onload = () => {
                        this.resizeImage()
                        imgPreview.style.visibility = 'visible'
                        this.resizeCutter()
                        this.refreshPercentage()
                        this.resizeResult()
                        //计算图片相对浏览器的限值
                        this.iLeft = imgPreview.getBoundingClientRect().left + document.documentElement.scrollLeft + cPBorderWidth
                        this.iTop = imgPreview.getBoundingClientRect().top + document.documentElement.scrollTop + cPBorderWidth
                        this.iRight = this.iLeft + imgPreview.clientWidth
                        this.iBottom = this.iTop + imgPreview.clientHeight
                    }
                    this.imgResult.src = imgPreview.src = fr.result
                }
                //如果错误,抛出异常
                fr.onerror = ev => alert(ev.target.error)                
                fr.readAsDataURL(file)
            }
        }, false)

        cutter.addEventListener('dragstart', ev => {
            cOffsetX = ev.offsetX
            cOffsetY = ev.offsetY
            /*
            let dragIcon = document.createElement("img") 
            dragIcon.src = 'image/drag.jpg'
            dragIcon.width = cutter.width  
            document.body.appendChild(dragIcon) 
            ev.dataTransfer.setDragImage(dragIcon, 0, 0); */
            //cutter.style.border = "2px red solid"   
            console.log('dragstart')
            return false
        }, false)

        cutter.addEventListener('dragover', ev => {
            //ev.stopPropagation()
            //ev.preventDefault()
            console.log('dragover')
            return false
        }, false)

        cutter.addEventListener('dragleave', ev => {
            //ev.stopPropagation()
            //ev.preventDefault()
            console.log('dragleave')
            return false
        }, false)

        cutter.addEventListener('drop', ev => {
            console.log('drop')
        }, false)

        cutter.addEventListener('dragend', ev => {
            cLeft = ev.clientX - cOffsetX - cBorderWidth
            cTop = ev.clientY - cOffsetY - cBorderWidth
            cRight = cLeft + cutter.clientWidth + cBorderWidth
            cBottom = cTop + cutter.clientHeight + cBorderWidth

            if (!this.iTop || cTop < this.iTop || cLeft < this.iLeft || cRight > this.iRight || cBottom > this.iBottom) {
                ev.stopPropagation()
                ev.preventDefault()
            } else {
                cutter.style.left = (cLeft - this.iLeft) + 'px'
                cutter.style.top =  (cTop - this.iTop) + 'px'
                imgResult.style.left = -((cLeft - this.iLeft) * this.percentage).toFixed(2) + 'px'
                imgResult.style.top = -((cTop - this.iTop) * this.percentage).toFixed(2) + 'px'
            }
        }, false)
    }

    checkFile(file) {
        if (!file.type.startsWith("image")) {
            alert('不是有效的图片')
            return false
        }
        if (file.size > IMAGE_MAX_SIZE * 1024 * 1024) {
            alert(`上传的图片不允许大于${IMAGE_MAX_SIZE}M`)
            return false
        }
        return true
    }

    resizeImage() {
        let img = this.imgPreview, h = img.height, w = img.width,
            ph = img.parentElement.clientHeight, pw = img.parentElement.clientWidth,
            phc = h / ph, pwc = w / pw
        phc > pwc ? img.height = ph : img.width = pw
    }

    resizeCutter() {
        let minValue = Math.min(Math.min(imgPreview.clientHeight, CUTTER_HEIGHT), Math.min(imgPreview.clientWidth, CUTTER_WIDTH))
        cutter.style.height = cutter.style.width = minValue + 'px'
        cutter.style.top = cutter.style.left = null
    }

    resizeResult() {
        imgResult.style.width = (imgPreview.clientWidth * this.percentage).toFixed(2) + 'px'
        imgResult.style.height = (imgPreview.clientHeight * this.percentage).toFixed(2) + 'px'
        imgResult.style.top = imgResult.style.left = null
    }

    refreshPercentage() {
        this.percentage = this.imgResult.parentElement.clientWidth / this.cutter.clientWidth
    }
}

(new SimpleImageCutter({
    fileUpload: fileImageCut,
    imgPreview: imgPreview,
    imgResult: imgResult,
    cutter: cutter
})).init()

 

这种简单实现存在的问题(下一种思路 html5 canvas):

1. 拖动效果体验比较差

2. 剪裁后的图片保存问题

源码路径:https://github.com/xiangwenhu/BlogCodes

上一篇:java 数据格式验证类


下一篇:(靠谱) Spring Boot与jsp打包成jar(war)使用