shopify Liquid 商品360旋轉(zhuǎn)拖拽查看功能

屏幕錄制2021-07-01 下午1.gif

po語法真麻煩

需求 只引入一個js文件 產(chǎn)品頁使用和推薦頁復(fù)用

liquid 引入 文件 并傳入變量
產(chǎn)品頁

{% include 'media-360', path360: product.metafields.custom_fields.path_360, featured_media: featured_media, height: height,preview_image:preview_image , type:"product",size:"medium",total:"72" ,interval:'1',seconds:"10"  %}

推薦頁

<div style="z-index:9;">
  {% include 'media-360',path360:path360,preview_image:img_url,height:250 ,productUrl:product.url , size:"small",total:"72",interval:"2",seconds:"12",type:"collection" %} 
</div>

media-360.liquid

<style>
  .watermark {
    position: absolute;
    top:0;
    left:0;
  }
  .preview_image{
    width: 100%;
    height: 100%;
    position:absolute;
    z-index:-1;
  }
  .collection-watermark{
    position: absolute;
    bottom:0;
    right:0;
    width: 50px;
    height:50px;
    opacity:0.3;
    padding:10px;
  }
</style>
<div
  class="product-single__media-wrapper js"
  data-product-single-media-wrapper
  data-media-id="product-template-360view"
  style="max-width:{{ height }}px; max-height:{{ height }}px;position:relative;z-index:999;"
  tabindex="-1"
  data-360-size="{{size}}"
  data-360-path="{{ path360 }}" 
  data-360-total="{{total}}"
  data-360-interval="{{interval}}"
  data-360-seconds="{{seconds}}"
  data-product-url="{{ productUrl }}"
  >
    {% if type == "collection" %}
      <img 
        onload="media360Load(this,'{{ type }}')" 
        class="grid-view-item__image lazyload preview_image"
        alt=""
        data-src="{{ preview_image }}"
        data-widths="[180, 360, 540, 720, 900, 1080, 1296, 1512, 1728, 2048]"
        data-aspectratio="{{ preview_image.aspect_ratio }}"
        data-sizes="auto"
        data-image>
      <img class="collection-watermark" 
        src="{{ '360-black.svg' | asset_url }}" 
        alt="360">
    {% else %}    
      <img
        class="preview_image"
        onload="media360Load(this,'{{ type }}')" 
        src="{{ preview_image }}"
        alt="">
    {% endif %}
    {% include "media-360-loading" %}
</div>

支持media360的js文件 邏輯實(shí)現(xiàn)

const sources = {};// key:產(chǎn)品 {{folderName}} value:{  key:索引 value:Image元素src為請求的blob圖片}

function media360Load(e, type) {
    const container = e.parentElement;
    const size = container.scrollWidth;
    const canvas = createCanvas({ width: size, height: size, });
    container.appendChild(canvas);
    const canvasContext = canvas.getContext("2d");
    const path360 = container.getAttribute("data-360-path");
    const size360 = container.getAttribute("data-360-size");//medium or small
    const total = Number(container.getAttribute("data-360-total"));
    const interval = Number(container.getAttribute("data-360-interval"));
    const seconds = container.getAttribute("data-360-seconds");
    const timeout = Math.round(seconds * 1000 / total);
    const folderName = path360.split('/').pop();
    sources[folderName] = {};//初始化對象
    const filename360Prefix = `${folderName}-${size360}-`;
    const progressBar = container.querySelector('div .load-progress-bar');
    const requestFailIndex = [];
    let timer = null;
    let currentIndex = 1;
    let prevMoveOrigin = 0;
    const onProductPage = type === 'product'
    let moveCount = 0;// collection 切換12個px次才動一次 否則瀏覽太快


    if (onProductPage) {
        //開始加載全部數(shù)據(jù)加載完后再播放并添加拖拽效果
        requestAllSources({ handleRequestComplete, urlPrefix: `${path360}/${filename360Prefix}`, interval, total, key: folderName, progressBar, requestFailIndex });
    } else if (type === 'collection') {
        //hover后再加載數(shù)據(jù) 然后移除hover監(jiān)聽
        function mouseenter() {
            //開始加載全部數(shù)據(jù)加載完后再播放并添加拖拽效果
            requestAllSources({ handleRequestComplete, urlPrefix: `${path360}/${filename360Prefix}`, interval, total, key: folderName, progressBar, requestFailIndex });
            //移除hover監(jiān)聽
            container.removeEventListener('mouseenter', mouseenter);
        }
        container.addEventListener('mouseenter', mouseenter);
    }

    //開始播放
    function startPaly(index) {
        // clearTimeout(timer);
        const sourceIndex = index <= total ? index : 1;
        drawSource(canvasContext, sources[folderName][sourceIndex], size);
        currentIndex = index;
        timer = setTimeout(() => {
            startPaly(sourceIndex + interval);
        }, timeout)
    }
    //請求完成后需要的操作
    function handleRequestComplete() {
        //開始播放
        startPaly(1);
        //添加拖拽效果
        addDragListener();
        //請求失敗的 再次請求
        requestSourceAgain();
        //collection 360水印去掉 product 鼠標(biāo)樣式改變
        if (!onProductPage) {
            const watermark = container.querySelector(".collection-watermark");
            container.removeChild(watermark);
            canvas.style['cursor'] = "ew-resize"
        } else {
            canvas.style["cursor"] = "grab";
        }
    }

    //再次請求請求失敗的資源
    function requestSourceAgain() {
        requestFailIndex.forEach((index) => {
            requestImageBlob(index)
                .then(({ data }) => {
                    generateImage(data).onload = ({ path: [image] }) => {
                        sources[folderName][index] = image;
                    };
                })
        })
    }

    function addDragListener() {
        if (onProductPage) {
            // 添加監(jiān)聽
            canvas.addEventListener("mousedown", starDrag);
            canvas.addEventListener("mouseup", stopDrag);
            canvas.addEventListener("mouseleave", stopDrag);
            // 移動端事件
            canvas.addEventListener("touchstart", starDrag);
            canvas.addEventListener("touchend", stopDrag);
            canvas.addEventListener("touchleave", stopDrag);
        } else {
            canvas.addEventListener("mouseenter", starDrag);
            canvas.addEventListener("mousemove", mouseMove);
            canvas.addEventListener("mouseleave", stopDrag);
        }


    }

    function starDrag(e) {
        e.preventDefault();
        // 阻止默認(rèn)行為, 用意移動端阻止mouse事件
        const distance = e.clientX || e.touches[0].clientX;
        // 停止自動播放
        clearTimeout(timer);
        timer = null;
        // 記錄當(dāng)前位置锈津,決定播放方向
        prevMoveOrigin = distance;
        // 添加移動事件
        canvas.addEventListener("touchmove", mouseMove);
        canvas.addEventListener("mousemove", mouseMove);
        if (onProductPage) {
            canvas.style['cursor'] = "grabbing";
        }
    }


    function mouseMove(e) {
        e.preventDefault();
        if (!onProductPage) {
            moveCount += 1;
            if (moveCount < 12) return;
            moveCount = 0;
        }
        if (timer) { clearTimeout(timer); timer = null; }
        const distance = e.clientX || e.touches[0].clientX;
        const nextSourcesIndex = computedNextSourcesIndex(
            distance,
            prevMoveOrigin,
            currentIndex,
        );
        drawSource(canvasContext, sources[folderName][nextSourcesIndex], size);
        currentIndex = nextSourcesIndex;
        prevMoveOrigin = distance;
    }


    function stopDrag() {
        canvas.removeEventListener("mousemove", mouseMove);
        canvas.removeEventListener("touchmove", mouseMove);
        // 從當(dāng)前位置開啟自動播放
        startPaly(currentIndex);
        if (onProductPage) {
            canvas.style['cursor'] = "grab";
        }
    }


    function computedNextSourcesIndex(
        clientX,
        prevClientX,
        currentSourceIndex,
    ) {
        let nextIndex = currentSourceIndex;
        if ((clientX - prevClientX) > 0) {
            nextIndex = currentSourceIndex - interval;
        } else if ((clientX - prevClientX) < 0) {
            nextIndex = currentSourceIndex + interval;
        }

        const allowValue = nextIndex <= total && nextIndex >= 1;

        return allowValue ? nextIndex : nextIndex < 1 ? (total + 1 - interval) : 1;
    }
    //collection 添加 點(diǎn)擊跳轉(zhuǎn)product頁面
    if (!onProductPage) {
        const link = document.createElement('a');
        container.appendChild(link);
        link.setAttribute('href', container.getAttribute("data-product-url"));
        container.addEventListener('click', () => {
            link.click();
        })
    }
}

function requestAllSources({ handleRequestComplete, urlPrefix, interval, total, key, progressBar, requestFailIndex }) {
    for (let index = 1; index <= total; index += interval) {
        requestImageBlob(urlPrefix, index)
            .then(({ data }) => {
                generateImage(data).onload = ({ path: [image] }) => {
                    sources[key][index] = image;
                    oneRequestComplete(key, total, progressBar, handleRequestComplete, interval);
                };
            })
            .catch(err => {
                console.error('request err - ' + index, err);
                sources[key][index] = sources[key][index - 1];
                requestFailIndex.push(index);
                oneRequestComplete(key, total, progressBar, handleRequestComplete, interval);
            });

    }
}

// 每次請求完一個資源后需要判斷是否是最后一個
function oneRequestComplete(key, total, progressBar, handleRequestComplete, interval) {
    const { length } = Object.keys(sources[key]);
    const intervalLength = length * interval;
    progressBarProgress(progressBar, intervalLength, total);
    if (intervalLength == total) {
        // console.log("request complete", sources[key]);
        progressBar.style['height'] = '0';
        progressBar.style['opacity'] = '0';
        handleRequestComplete();
    }
}

// 加載進(jìn)度條進(jìn)度變化
function progressBarProgress(progressBar, length, total) {
    const progress = ((length / total) % total) * 100;
    progressBar.style["width"] = `${progress}%`;
}

//生成sources需要存儲的 保存了blob圖片的img元素
function generateImage(bold) {
    const image = new Image();
    const imgSrc = window.URL.createObjectURL(bold);
    image.src = imgSrc;
    return image;
}

function createCanvas(canvasProperties) {
    const canvas = document.createElement("canvas");
    for (const [property, value] of Object.entries(canvasProperties)) {
        canvas[property] = value;
    }
    return canvas;
}

function requestImageBlob(urlPrefix, index) {
    return axios.get(`${urlPrefix}${index}.jpg?`, { responseType: 'blob' })
}

// canvas繪制圖片
function drawSource(canvasContext, image, size) {
    try {
        canvasContext.drawImage(image, 0, 0, size, size);
    } catch (error) {
        console.log("draw source err:", error)
    }
}

https://kalosgem.com/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末灌旧,一起剝皮案震驚了整個濱河市坪哄,隨后出現(xiàn)的幾起案子贤旷,更是在濱河造成了極大的恐慌粮呢,老刑警劉巖喇潘,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枕赵,死亡現(xiàn)場離奇詭異渴析,居然都是意外死亡晚伙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門俭茧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咆疗,“玉大人,你說我怎么就攤上這事母债∥绱牛” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵毡们,是天一觀的道長迅皇。 經(jīng)常有香客問我,道長衙熔,這世上最難降的妖魔是什么登颓? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮红氯,結(jié)果婚禮上框咙,老公的妹妹穿的比我還像新娘。我一直安慰自己痢甘,他們只是感情好喇嘱,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著塞栅,像睡著了一般者铜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天王暗,我揣著相機(jī)與錄音悔据,去河邊找鬼。 笑死俗壹,一個胖子當(dāng)著我的面吹牛科汗,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绷雏,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼头滔,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涎显?” 一聲冷哼從身側(cè)響起坤检,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎期吓,沒想到半個月后早歇,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讨勤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年箭跳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片潭千。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡谱姓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出刨晴,到底是詐尸還是另有隱情屉来,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布狈癞,位于F島的核電站茄靠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏亿驾。R本人自食惡果不足惜嘹黔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望莫瞬。 院中可真熱鬧儡蔓,春花似錦、人聲如沸疼邀。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽旁振。三九已至获询,卻和暖如春涨岁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吉嚣。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工梢薪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人尝哆。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓秉撇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親秋泄。 傳聞我的和親對象是個殘疾皇子琐馆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345