天天看點

JavaScript實作仿微信頭像裁剪(前端完成裁剪)

<!DOCTYPE html>
<html lang="en">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=0.5,minimum-scale=0.5,maximum-scale=0.5,user-scalable=no" />
    <title>Title</title>
    <style>
        body {
            position: fixed;
            width: 100%;
            height: 100%;
            margin: 0px;
            background-color: black;
        }
        .cover-top {
            font-size: 50px;
            color: white;
            text-align: center;
            width: 100%;
            padding:5px;
            border: solid 0px;
            background-color: black;
            position: absolute;
            opacity: 0.5;
            top: 0px;
            z-index: 1;
        }
        .cover-top input {
            font-size: 50px;opacity: 0;position: absolute;right: 0;top: 0;
        }
        .cover-middle {
            width: 100%;
            padding:5px;
            border: solid 0px;
            position: absolute;
            transform: translateY(-50%);
            top: 50%;
            z-index: 1;
        }
        .cover-down {
            font-size: 28px;
            color: white;
            text-align: center;
            width: 100%;
            padding:5px;
            border: solid 0px;
            background-color: black;
            position: absolute;
            opacity: 0.5;
            bottom: 0px;
            z-index: 1;
        }
        button {
            padding: 20px;position: absolute;left: 0px;
        }
        .image {
            display:block;width: 100%;overflow: scroll;transform: translate(0px,50%);position: absolute;
        }
    </style>
</head>
<body>
    <form id="cutImage" action="htpp://localhost/product/upload_pic" enctype="multipart/form-data" method="post">
        <div id="cover-top" class="cover-top">
            <a  href="javascript:">
                <input id="imageFile" name="image" type="file" accept="image/gif, image/jpeg, image/jpg" onchange="loadImage()">點選上傳
            </a>
            <button onclick="selectImage()">選擇圖檔</button>

            <input id="x", name="x" style="display: none">
            <input id="y", name="y" style="display: none">
            <input id="width" name="width" style="display: none">
            <input id="height" name="height" style="display: none">
        </div>
        <div id="cover-middle" class="cover-middle"></div>
        <div id="cover-down" class="cover-down">
            <button type="button" onclick="cutParams()">獲得裁剪參數</button><span id="params">12121</span>
        </div>
        <img id="image" class="image" draggable="true" src="./23123.jpeg" onload="init()">
    </form>

</body>
<script>
    //圖檔的尺寸({寬,高, 真實的寬,真實的高})
    var imageParams={width:0, height:0, naturalWidth:0, naturalHeight:0};
    //螢幕的寬高
    var containSize={width:0, height:0};
    //滑鼠觸摸時滑鼠坐标
    var mouseOnCrd={x1:0, y1:0, x2:0, y2:0}
    //圖檔的坐标(其實就是偏移的位置)
    var imageCrd = {x:0, y:0};

    //頁面加載時候需要初始化的參數
    window.onload = function () {
        containSize.width = document.body.offsetWidth;
        containSize.height = document.body.offsetHeight;
        //設定遮罩層的寬高
        var coverMiddle = document.getElementById("cover-middle");
        coverMiddle.style.width=containSize.width + "px";
        coverMiddle.style.height=containSize.width + "px";
        var coverTop = document.getElementById("cover-top");
        coverTop.style.height=(containSize.height-containSize.width)/2 + "px";
        var coverDown = document.getElementById("cover-down");
        coverDown.style.height=(containSize.height-containSize.width)/2 + "px";
        init()
    }

    //獲得目前的參數(必須要通過綁定img 的onload事件,不然讀取到的參數不準确)
    function init() {
        //設定初始參數
        var image = document.getElementById("image")
        imageParams.width = image.width;
        imageParams.height = image.height;
        imageParams.naturalWidth = image.naturalWidth;
        imageParams.naturalHeight = image.naturalHeight;
        //配置圖檔的位置
        configImage();
    }
    /**
     * 配置圖檔的位置
     */
    function configImage() {
        var image = document.getElementById("image");
        imageCrd.x = 0; // 初始位置橫向從零開始
        imageCrd.y = parseInt((containSize.height - imageParams.height)/2); // 設定縱向居中
        image.style.transform = "translate(" + imageCrd.x + "px," + imageCrd.y + "px)"; // 完成偏移操作
    }

    //點選選擇圖檔
    function selectImage() {
        document.getElementById("imageFile").click();

    }

    //選擇圖檔之後加載圖檔
    function loadImage() {
        var files = document.getElementById("imageFile").files;
        var reader = new FileReader();
        reader.readAsDataURL(files[0]);
        reader.onload = function() {
            document.getElementById("image").setAttribute('src', reader.result);
        };
    }

    //觸摸移動事件
    document.addEventListener("touchmove", function (ev) {
        if(ev.touches.length == 2) { //放大
            zoom(ev.touches[0], ev.touches[1])
        }
        if(ev.touches.length == 1) { //移動
            move(ev.touches[0]); //由于手機是多點觸控,是以很可能有多個事件
        }
    })


    //螢幕觸摸事件
    document.addEventListener("touchstart", function (ev) {
        //雙點觸摸(即放大或者縮小圖檔)
        if(ev.touches.length == 2) {
            mouseOnCrd.x1 = ev.touches[0].pageX;
            mouseOnCrd.y1 = ev.touches[0].pageY;
            mouseOnCrd.x2 = ev.touches[1].pageX;
            mouseOnCrd.y2 = ev.touches[1].pageY;
        }
        //單點觸摸(即移動圖檔)
        if(ev.touches.length == 1) {
            var e = ev.touches[0];
            mouseOnCrd.x1 = e.pageX;
            mouseOnCrd.y1 = e.pageY;
        }
    });

    //縮放圖檔
    function zoom(touch1, touch2) {
        var touchCrd1 = getMouseCrd(touch1);
        var touchCrd2 = getMouseCrd(touch2);
        //計算新舊兩點的距離,判斷是縮小還是放大(false:縮小;true:放大)
        var zoomFlag = (Math.pow((mouseOnCrd.x1 - mouseOnCrd.x2), 2) + Math.pow((mouseOnCrd.y1 - mouseOnCrd.y2), 2)) -
        (Math.pow((touchCrd1.x - touchCrd2.x), 2) + Math.pow((touchCrd1.y - touchCrd2.y), 2)) > 0 ? false : true;
//        alert("運作到這裡" + zoomFlag);
        //設定圖檔的寬度,高度是自适應,是以不用擷取
        var image = document.getElementById("image");
        image.style.width =  (zoomFlag ? (imageParams.width + 15) : (imageParams.width - 15) )+ "px";
        //重新獲得圖檔的dom元素,并擷取寬高
        imageParams.width = image.width;
        imageParams.height = image.height;
//        document.getElementById("cover-down").innerHTML= imageParams.width + "px" + ":" +imageParams.height + "px";
    }

    //移動圖檔
    function move(touch) {
        var touchCrd = getMouseCrd(touch);
        //和初始坐标比較,在每個坐标上發生的位移
        console.log("發生了位移:", touchCrd.x-mouseOnCrd.x1, touchCrd.y-mouseOnCrd.y1);
        delPicCoordinate({x:touchCrd.x-mouseOnCrd.x1, y: touchCrd.y-mouseOnCrd.y1});
        //獲得位移量之後,重新指派給初始坐标,以備下次比較
        mouseOnCrd.x1 = touchCrd.x;
        mouseOnCrd.y1 = touchCrd.y;
    }



    //根據位移計算圖檔的坐标
    function delPicCoordinate(shifting) {
        var image = document.getElementById("image");
        //目前的圖檔的坐标
        var x = imageCrd.x;
        var y = imageCrd.y;
        x = checkX(x, parseInt(shifting.x));
        y = checkY(y, parseInt(shifting.y))
        console.log("即将偏移的量:", shifting.x, shifting.y);
        image.style.transform = "translate(" + x + "px," + y + "px)";
//        document.getElementById("cover-down").innerHTML=x + "px:" + y + "px";
        console.log("偏移之後的偏移:", x, y);
        imageCrd.x = x;
        imageCrd.y = y;
    }

    //判斷是否可以在x軸上移動
    function checkX(x, shiftingX) {
        if(shiftingX < 0) { //向左
            if((x + shiftingX) < (containSize.width- imageParams.width)) {
                //如果轉行前已經比他小,保持原樣
                if(x < (containSize.width- imageParams.width)){
                    return x;
                }
                return (containSize.width- imageParams.width);
            }
        }else {
            if((x + shiftingX) > 0) { //向右
                //如果轉行前已經比他大,保持原樣
                if(x > shiftingX) {
                    return y;
                }
                return 0
            }
        }
        return x+ shiftingX
    }

    //判斷是否可以在x軸上移動
    function checkY(y, shiftingY) {
        if(shiftingY < 0) { //向上
            if((y + shiftingY) < ((containSize.width+containSize.height)/2 - imageParams.height)) {
                console.log("==============向上:", y+shiftingY, ((containSize.width+containSize.height)/2 - imageParams.height));
                //如果已經是比這個小,保持原樣
                if(y < ((containSize.width+containSize.height)/2 - imageParams.height)) {
                    return y;
                }
                return parseInt((containSize.width+containSize.height)/2 - imageParams.height);
            }
        }else { //向下
            if((y + shiftingY) > (containSize.height-containSize.width)/2) {//這裡隻是長寬都比頁面高
                //如果已經是比這個大,保持原樣
                if(y > (containSize.height-containSize.width)/2) {
                    return y;
                }
                return parseInt((containSize.height-containSize.width)/2);
            }
        }
        return y+ shiftingY
    }


    //獲得滑鼠坐标
    function getMouseCrd(touch) {
        //滑鼠移動的位置
        if(touch.pageX || touch.pageY){
            return {x:touch.pageX, y:touch.pageY};
        }
        return{
            x:touch.clientX + document.body.scrollLeft - document.body.clientLeft,
            y:touch.clientY + document.body.scrollTop - document.body.clientTop
        };
    }

    //計算最後的切割其實坐标,寬度和高度
    function cutParams() {
        var x;
        var y;
        var width;
        var height;
        if(imageParams.width > containSize.width) {
            x = 0 - imageCrd.x;
            width = containSize.width;
        }else {
            x = 0;
            width = containSize.width;
        }
        if(imageParams.height > containSize.width) {
            y = (containSize.height - containSize.width)/2 - imageCrd.y;
            height = containSize.width;
        }else {
            y = 0;
            height = imageParams.height;
        }
        var canvas = document.createElement("canvas");	// 畫布
		canvas.width = width;
		canvas.height = height;
        var zoomRate = imageParams.width / imageParams.naturalWidth; // 縮放比例
		// 擷取圖檔的真實切割起始坐标和寬高
        x = parseInt(x / zoomRate); 
        y = parseInt(y / zoomRate);
        width = parseInt(width / zoomRate);
        height = parseInt(height / zoomRate);
		var ctx = canvas.getContext('2d'); // 擷取畫布
		ctx.drawImage(imageOrigin,x,y,width,height,0,0,canvas.width,canvas.height); // 裁剪并畫入畫布
        saveimg(canvas);
    }
    // base64資料轉換為位圖
    function b64ToUint8Array(b64Image) {
       let img = atob(b64Image.split(',')[1]);
       let img_buffer = [];
       let i = 0;
       while (i < img.length) {
          img_buffer.push(img.charCodeAt(i));
          i++;
       }
       return new Uint8Array(img_buffer);
    }
    // 儲存圖檔到伺服器
	function saveimg(canvas){
	    // canva轉換為base64資料
	    let b64Image = canvas.toDataURL('image/jpeg');
	    // base64資料轉換為位圖
	    let u8Image  = b64ToUint8Array(b64Image);
	    // 組裝form表單資料
	    let formData = new FormData();
	    formData.append("image", new Blob([ u8Image ], {type: "image/jpg"}));
	    // ajax送出
	    let xhr1 = new XMLHttpRequest();
	    xhr1.open("POST", HREF_PREFIX+"/api/updateHeadImg.php", true);
	    xhr1.onreadystatechange = function(){
	        if(xhr1.readyState === XMLHttpRequest.DONE && xhr1.status === 200) {
	            document.getElementsByClassName("updateHeadImg")[0].style.display = "none";
	            document.getElementById("updateHeadImg").getElementsByTagName("img")[0].src = xhr1.responseText;
	        }
	        
	    }
	    xhr1.send(formData);     
	}
	
</script>
</html>
           

引用 https://gitee.com/sanpy/h5-demo/tree/master

增加了本地裁剪功能