po语法真麻烦
需求 只引入一个js文件 产品页使用和推荐页复用
liquid 引入 文件 并传入变量
产品页
{% 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" %}
推荐页
{% include 'media-360',path360:path360,preview_image:img_url,height:250 ,productUrl:product.url , size:"small",total:"72",interval:"2",seconds:"12",type:"collection" %}
media-360.liquid
{% if type == "collection" %}
{% else %}
{% endif %}
{% include "media-360-loading" %}
支持media360的js文件 逻辑实现
const sources = {};// key:产品 {{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) {
//开始加载全部数据加载完后再播放并添加拖拽效果
requestAllSources({ handleRequestComplete, urlPrefix: `${path360}/${filename360Prefix}`, interval, total, key: folderName, progressBar, requestFailIndex });
} else if (type === 'collection') {
//hover后再加载数据 然后移除hover监听
function mouseenter() {
//开始加载全部数据加载完后再播放并添加拖拽效果
requestAllSources({ handleRequestComplete, urlPrefix: `${path360}/${filename360Prefix}`, interval, total, key: folderName, progressBar, requestFailIndex });
//移除hover监听
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 鼠标样式改变
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) {
// 添加监听
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();
// 阻止默认行为, 用意移动端阻止mouse事件
const distance = e.clientX || e.touches[0].clientX;
// 停止自动播放
clearTimeout(timer);
timer = null;
// 记录当前位置,决定播放方向
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);
// 从当前位置开启自动播放
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 添加 点击跳转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();
}
}
// 加载进度条进度变化
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/