/** * 雪碧图注册表 * 用途:sprite-icon 按 sheet + name 从多张 CDN 雪碧图裁切显示图标 * 图片地址随 ext.json / 环境 imgHost 变化,必须用函数运行时读取,不能用 this.$constant */ import constant from "@/constant/index"; /** 默认图床(ext 未配置 imgHost 时的兜底) */ const DEFAULT_IMG_HOST = "https://img.huaml.com"; /** 未传 sheet 时默认使用的雪碧图 key */ const DEFAULT_SHEET_KEY = "main"; /** * 雪碧图注册表:每张图含 path、原图尺寸、icons 坐标 */ export const SPRITE_SHEETS = { main: { path: "/hhb/sprite_icon/main-index.webp?time=2607171640", width: 500, height: 500, icons: { "dengji-biandong": { x: 14, y: 17, w: 28, h: 30 }, "yue-biandong": { x: 60, y: 17, w: 28, h: 30 }, "huiyuan-quanyi": { x: 102, y: 17, w: 28, h: 30 }, "lianxikefu": { x: 145, y: 17, w: 28, h: 30 }, "huangguan": { x: 189, y: 20, w: 22, h: 21 }, "shezhi": { x: 13, y: 62, w: 34, h: 36 }, "shaixuan": { x: 71, y: 67, w: 25, h: 25 }, "dianpu-fill": { x: 116, y: 63, w: 31, h: 30 }, "huoche-cheng": { x: 171, y: 68, w: 23, h: 21 }, "huoche-lv": { x: 204, y: 68, w: 23, h: 21 }, "gou-quan": { x: 246, y: 68, w: 22, h: 21 }, "huashu": { x: 4, y: 118, w: 52, h: 49 }, "hua": { x: 94, y: 118, w: 52, h: 49 }, "dianpu-line": { x: 167, y: 114, w: 51, h: 51 }, "tuichu": { x: 243, y: 111, w: 49, h: 51 }, "xiaoxi": { x: 8, y: 186, w: 43, h: 44 }, "qianbao": { x: 68, y: 182, w: 55, h: 48 }, "huoche-da": { x: 142, y: 182, w: 58, h: 48 }, "baoguo": { x: 219, y: 182, w: 49, h: 48 }, "fanghu-yuan": { x: 282, y: 182, w: 42, h: 48 }, "youhuiquan-yellow": { x: 344, y: 184, w: 50, h: 50 }, "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 }, "icon-home": { x: 274, y: 102, w: 52, h: 52 }, "icon-home-active": { x: 274, y: 102, w: 52, h: 52 }, "gengduo-quan": { x: 274, y: 102, w: 52, h: 52 }, "icon-home": { x: 7, y: 245, w: 50, h: 50 }, "icon-home-active": { x: 60, y: 245, w: 50, h: 50 }, "icon-category": { x: 116, y: 245, w: 50, h: 50 }, "icon-category-active": { x: 176, y: 245, w: 50, h: 50 }, "icon-cart": { x: 238, y: 245, w: 50, h: 50 }, "icon-cart-active": { x: 293, y: 245, w: 50, h: 50 }, "icon-order": { x: 366, y: 245, w: 50, h: 50 }, "icon-order-active": { x: 418, y: 245 , w: 50, h: 50 }, "cart-delete": { x: 7, y: 298 , w: 46, h: 52 },//购物车-删除 "notice": { x: 216, y: 15 , w: 31, h: 31 },//公告通知 } }, /** 进货/供货商专用雪碧图,坐标待设计稿测量后补全 */ order: { path: "/hhb/sprite_icon/icon-order.webp?time=26072009", width: 600, height: 600, icons: { "ps-shop": { x: 257, y: 8 , w: 57, h: 50 },//商家配送 "ps-shop-active": { x: 190, y: 8 , w: 57, h: 50 },//商家配送-active "ps-paotui": { x: 198, y: 63 , w: 59, h: 50 },//跑腿配送 "ps-paotui-active": { x: 129, y: 63 , w: 59, h: 50 },//跑腿配送-active "ps-daodian": { x: 383, y: 8 , w: 48, h: 50 },//到店自取 "ps-daodian-active": { x: 324, y: 8 , w: 49, h: 50 },//到店自取-active "ps-kuaidi": { x: 72, y: 63 , w: 47, h: 50 },//快递 "ps-kuaidi-active": { x: 12, y: 63 , w: 46, h: 50 },//快递-active "ps-wuliu": { x: 441, y: 8 , w: 67, h: 50 },//物流 "ps-wuliu-active": { x: 518, y: 8 , w: 66, h: 50 },//物流-active "huoche": { x: 7, y: 8 , w: 46, h: 50 },//配送说明-小货车 "youhuiquan": { x: 267, y: 63 , w: 58, h: 50 },//优惠券 "beizhu": { x: 12, y: 8 , w: 50, h: 50 },//订单-备注 "shijian": { x: 70, y: 8 , w: 52, h: 52 },//配送时间 "shijianduan": { x: 129, y: 8 , w: 52, h: 52 },//配送时间段 "dingwei": { x: 339, y: 63 , w: 42, h: 51 },//定位 } } }; /** tabBar 未选中 / 选中 图标名映射(main 雪碧图) */ export const TAB_SPRITE_MAP = { home: { normal: "dianpu-line", active: "dianpu-fill" }, order: { normal: "huoche-lv", active: "huoche-da" }, mine: { normal: "gou-quan", active: "qianbao" } }; /** @deprecated 兼容旧引用 */ export const MAIN_INDEX_SPRITE = { width: SPRITE_SHEETS.main.width, height: SPRITE_SHEETS.main.height }; /** @deprecated 兼容旧引用 */ export const MAIN_INDEX_ICONS = SPRITE_SHEETS.main.icons; function buildIconSheetIndex(sheets) { const index = {}; Object.keys(sheets).forEach((sheetKey) => { const icons = sheets[sheetKey].icons || {}; Object.keys(icons).forEach((name) => { if (index[name] && index[name] !== sheetKey && process.env.NODE_ENV !== "production") { console.warn(`[sprite] 图标名 "${name}" 在 ${index[name]} 与 ${sheetKey} 中重复,以 ${sheetKey} 为准`); } index[name] = sheetKey; }); }); return index; } const ICON_SHEET_INDEX = buildIconSheetIndex(SPRITE_SHEETS); function getImgHostBase() { let base = (constant.imgUrl || DEFAULT_IMG_HOST).replace(/\/$/, ""); if (base.indexOf("http://") === 0) { base = `https://${base.slice(7)}`; } return base; } function rpx2px(rpx) { if (typeof uni !== "undefined" && typeof uni.upx2px === "function") { return uni.upx2px(rpx); } return rpx; } /** * 计算图标显示宽高与缩放比(支持按宽度 size 或按高度 height 等比缩放) * @param {{ w: number, h: number }} icon 雪碧图坐标中的原始宽高 * @param {{ size?: number|string, height?: number|string }} opts * @returns {{ displayW: number, displayH: number, scale: number }} */ function resolveSpriteDisplaySize(icon, opts = {}) { const heightNum = Number(opts.height); const useHeight = opts.height !== "" && opts.height != null && !Number.isNaN(heightNum) && heightNum > 0; let scale; let displayW; let displayH; if (useHeight) { displayH = Math.round(heightNum); scale = displayH / icon.h; displayW = Math.round(icon.w * scale); } else { const sizeNum = Number(opts.size); displayW = !Number.isNaN(sizeNum) && sizeNum > 0 ? sizeNum : 48; scale = displayW / icon.w; displayH = Math.round(icon.h * scale); } return { displayW, displayH, scale }; } export function resolveSprite(name, sheetKey = "") { if (!name) { return null; } let key = sheetKey; if (!key) { key = ICON_SHEET_INDEX[name] || DEFAULT_SHEET_KEY; } const sheet = SPRITE_SHEETS[key]; if (!sheet) { return null; } const icon = (sheet.icons || {})[name]; if (!icon) { return null; } return { sheetKey: key, sheet, icon }; } export function getSpriteSheetUrl(sheetKey = DEFAULT_SHEET_KEY) { const sheet = SPRITE_SHEETS[sheetKey]; if (!sheet) { return ""; } return `${getImgHostBase()}${sheet.path}`; } export function spriteImageStyle(name, size = 48, sheetKey = "", height = "") { const resolved = resolveSprite(name, sheetKey); if (!resolved) { if (process.env.NODE_ENV !== "production") { console.warn(`[sprite-icon] 未找到图标: name=${name}${sheetKey ? ` sheet=${sheetKey}` : ""}`); } return { wrap: "", inner: "", image: "" }; } const { sheet, icon } = resolved; const { displayW, displayH, scale } = resolveSpriteDisplaySize(icon, { size, height }); const sheetW = sheet.width * scale; const sheetH = sheet.height * scale; const left = -rpx2px(icon.x * scale); const top = -rpx2px(icon.y * scale); const sheetWPx = rpx2px(sheetW); const sheetHPx = rpx2px(sheetH); return { wrap: `position:relative;width:${displayW}rpx;height:${displayH}rpx;overflow:hidden;line-height:0;`, inner: `position:absolute;left:${left}px;top:${top}px;width:${sheetWPx}px;height:${sheetHPx}px;`, image: "width:100%;height:100%;display:block;" }; } export function spriteStyle(name, size = 48, sheetKey = "", height = "") { const resolved = resolveSprite(name, sheetKey); if (!resolved) { return ""; } const { sheetKey: key, sheet, icon } = resolved; const { displayW, displayH, scale } = resolveSpriteDisplaySize(icon, { size, height }); const sheetW = sheet.width * scale; const sheetH = sheet.height * scale; const posX = rpx2px(icon.x * scale); const posY = rpx2px(icon.y * scale); const sheetWPx = rpx2px(sheetW); const sheetHPx = rpx2px(sheetH); const url = getSpriteSheetUrl(key); return [ `width:${displayW}rpx`, `height:${displayH}rpx`, `background-image:url(${url})`, `background-size:${sheetWPx}px ${sheetHPx}px`, `background-position:-${posX}px -${posY}px`, "background-repeat:no-repeat", "display:inline-block", "flex-shrink:0", "vertical-align:middle" ].join(";"); } export function getMainIndexSpriteUrl() { return getSpriteSheetUrl(DEFAULT_SHEET_KEY); } export function mainIndexSpriteImageStyle(name, size = 48, height = "") { return spriteImageStyle(name, size, DEFAULT_SHEET_KEY, height); } export function mainIndexSpriteStyle(name, size = 48, height = "") { return spriteStyle(name, size, DEFAULT_SHEET_KEY, height); }