Mapbox Sprite精灵图生成

2023-11-04

出处:ATtuing - 博客园 ,https://www.cnblogs.com/ATtuing/p/9273391.html

1.什么是sprite文件 

        sprite 文件主要是将一堆小图生成一种大图的方法,并且将每张小图的位置信息保存下来,方便读取。在网络请求中会减少请求的数量,mapbox借鉴前端中CSS Sprite方法存储图标信息的。sprite.png文件保存图标,sprite.json保存名称及位置信息,下图图展示的是小图标与大图文件的示例。下面我讲一下两种文件转换。

转为

 2.实现的功能

          再此基础上将小图转大图功能用JavaScript实现。使用Vue、Element实现。

演示地址:https://c317.gitee.io/myb_style/html/creat_MBSprite.html 

3.具体实现方法 

1.js获取图片像素

function getXY(canvas, x, y) {
                let ctx = canvas.getContext("2d");
                // 获取画布上的图像像素矩阵
                let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                let w = imageData.width
                let data = imageData.data
                let color = []
                color[0] = data[(y * w + x) * 4]
                color[1] = data[(y * w + x) * 4 + 1]
                color[2] = data[(y * w + x) * 4 + 2]
                color[3] = data[(y * w + x) * 4 + 3]
                return color
            }

2.js设置图片像素

//创建canvas
let editMap = document.createElement('canvas');
editMap.width = allwidth;//设置宽度
editMap.height = allheight;//设置高度
let editCxt = editMap.getContext("2d");
//获取ImageData
let imageData = editCxt.getImageData(0, 0, allwidth, allheight);
function setXY(imageData, x, y, color) {
                let w = imageData.width
                let data = imageData.data
                data[(y * w + x) * 4] = color[0]
                data[(y * w + x) * 4 + 1] = color[1]
                data[(y * w + x) * 4 + 2] = color[2]
                data[(y * w + x) * 4 + 3] = color[3]
                imageData.data = data;
            }

3.小图转大图

       将小图标合成一张sprite大图并在sprite.json中记录生成的位置信息,这里最主要的就是图标的摆放规则。

(1)获取所有的图标文件,按照高度从小到大排列

(2)根据大图生成的默认宽度,循环小图片,形成一行一行的图片集合。

(3)根据行数和宽度生成大图的宽度。

(4)循环小图标,在大图中画出小图标,并记录位置信息。

实现成果与核心代码如下:

function creatSprite(paramlist) {
                //图片默认宽度为255
                let allwidth = 255;
                let rowparams = [], paramnowlist = [];
                let countnum = 0;
                for (let i = 0; i < paramlist.length; i++) {
                    countnum += paramlist[i].width;
                    if (countnum > allwidth) {
                        i = i - 1;
                        countnum = 0;
                        rowparams.push(paramnowlist);
                        paramnowlist = [];
                    } else {
                        paramnowlist.push(paramlist[i]);
                    }
                    if (i === paramlist.length - 1) {
                        rowparams.push(paramnowlist);
                        break;
                    }
                }
                //计算应有的高度
                let allheight = 0;
                rowparams.forEach(item => {
                    allheight += Math.max.apply(Math, item.map(m => m.height));
                })
                //计算应有的宽度
                allwidth = 0
                rowparams[0].forEach(item => {
                    allwidth += item.width;
                })
                if (allwidth > 200) allwidth = 255;
                console.log(allwidth)
                let spritejson = "{\n";
                //开始画大图
                let editMap = document.createElement('canvas');
                editMap.width = allwidth;
                editMap.height = allheight;
                let editCxt = editMap.getContext("2d");
                let editImageData = editCxt.getImageData(0, 0, allwidth, allheight);
                //保存起始高度
                let heighttemp = 0;
                for (let i = 0; i < rowparams.length; i++) {
                    let tempwidthnum = 0;
                    for (let j = 0; j < rowparams[i].length; j++) {
                        let map = rowparams[i][j].canvas;
                        //循环小图片
                        for (let x = 0; x < map.width; x++) {
                            for (let y = 0; y < map.height; y++) {
                                //获取像素
                                let color = this.getXY(map, x, y);
                                this.setXY(editImageData, x + tempwidthnum, y + heighttemp, color);
                            }
                        }
                        spritejson += "  \"" + rowparams[i][j].name.replace("-", "/").replace("&", ":") + "\":{\"x\":";
                        spritejson += tempwidthnum + ",\"y\":" + heighttemp + ",\"width\":" + rowparams[i][j].width;
                        spritejson += ",\"height\":" + rowparams[i][j].height + ",\"pixelRatio\":1,\"sdf\":false},\n";
                        //增加宽度
                        tempwidthnum += rowparams[i][j].width;
                    }
                    heighttemp += Math.max.apply(Math, rowparams[i].map(m => m.height));
                }
                //保存大图
                editCxt.putImageData(editImageData, 0, 0);
                this.editURL = editMap.toDataURL("image/png");//取得图像的数据URI

                spritejson = spritejson.substring(0, spritejson.lastIndexOf(','));
                spritejson += "\n}";
                this.spritejson = spritejson
            }

4.完整代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>生成Mapbox Sprite(精灵图)</title>
    <!-- import CSS -->
    <link href="https://cdn.bootcss.com/element-ui/2.4.5/theme-chalk/index.css" rel="stylesheet">
    <style>
        html, body, #app{
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            position: absolute;
        }
        .upload, .creat{
            border-radius: 4px;
            background: #d3dce6;
            height: 100%;
        }
        .but{
            height: 100%;
            display: flex;
            align-items:center;
            justify-content:center;
        }
    </style>
</head>
<body>
<div id="app">
    <el-row style="height: 100%">
        <el-col class="upload" :span="11">
            <el-upload
                    action="https://jsonplaceholder.typicode.com/posts/"
                    list-type="picture-card"
                    accept="image/*"
                    :on-preview="handlePreview"
                    :on-success="handleSuccess"
                    :on-remove="handleRemove" multiple>
                <i class="el-icon-plus"></i>
                <div slot="tip" class="el-upload__tip">只能上传图片格式文件,且不超过500kb</div>
            </el-upload>
            <el-dialog :visible.sync="dialogVisible">
                <img width="100%" :src="dialogImageUrl" alt="">
            </el-dialog>
        </el-col>
        <el-col class="but" :span="2">
            <el-button type="primary" @click="image">转换</el-button>
        </el-col>
        <el-col class="creat" :span="11">
            <el-row style="height: 45%">
                <img :src="editURL" style="border:1px solid #6f6f6f">
            </el-row>
            <el-row style="height: 10%">
                <el-button type="primary" @click="downloadImg">下载图片</el-button>
                <el-button type="primary" @click="downloadJSON">下载JSON</el-button>
            </el-row>
            <el-row style="height: 45%">
                <el-input
                    type="textarea"
                    :rows="18"
                    placeholder="JSON内容"
                    v-model="spritejson"></el-input>
            </el-row>
        </el-col>
    </el-row>
</div>
</body>
<!-- import Vue before Element -->
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
<!-- import JavaScript -->
<script src="https://cdn.bootcss.com/element-ui/2.4.5/index.js"></script>
<script>
    new Vue({
        el: '#app',
        data() {
            return {
                dialogImageUrl: '',
                dialogVisible: false,
                disabled: false,
                fileList: [],
                canvas: [],
                paramList: [],//List<Param>
                rowparams: [],//List<List<Param>>
                editURL:'',
                spritejson:''
            }
        },
        mounted() {
        },
        methods: {
            handleRemove(file, fileList) {
                this.fileList = fileList;
            },
            handlePreview(file) {
                this.dialogImageUrl = file.url;
                this.dialogVisible = true;
            },
            handleSuccess(response, file, fileList) {
                this.fileList = fileList;
            },
            image() {
                let paramlist = [];
                if (this.fileList.length === 0) return;
                this.fileList.forEach(file => {
                    let image = new Image();
                    image.src = file.url;
                    let canvas = document.createElement('canvas');
                    canvas.width = image.width;
                    canvas.height = image.height;
                    canvas.getContext("2d").drawImage(image, 0, 0);
                    paramlist.push({
                        name: file.name,
                        x: 0, y: 0,
                        width: image.width,
                        height: image.height,
                        canvas: canvas
                    })
                })
                paramlist.sort(function (a, b) {
                    return a.height - b.height
                })
                this.paramList = paramlist;
                this.creatSprite(paramlist);
            },
            creatSprite(paramlist) {
                //图片默认宽度为255
                let allwidth = 255;
                let rowparams = [], paramnowlist = [];
                let countnum = 0;
                for (let i = 0; i < paramlist.length; i++) {
                    countnum += paramlist[i].width;
                    if (countnum > allwidth) {
                        i = i - 1;
                        countnum = 0;
                        rowparams.push(paramnowlist);
                        paramnowlist = [];
                    } else {
                        paramnowlist.push(paramlist[i]);
                    }
                    if (i === paramlist.length - 1) {
                        rowparams.push(paramnowlist);
                        break;
                    }
                }
                //计算应有的高度
                let allheight = 0;
                rowparams.forEach(item => {
                    allheight += Math.max.apply(Math, item.map(m => m.height));
                })
                //计算应有的宽度
                allwidth = 0
                rowparams[0].forEach(item => {
                    allwidth += item.width;
                })
                if (allwidth > 200) allwidth = 255;
                console.log(allwidth)
                let spritejson = "{\n";
                //开始画大图
                let editMap = document.createElement('canvas');
                editMap.width = allwidth;
                editMap.height = allheight;
                let editCxt = editMap.getContext("2d");
                let editImageData = editCxt.getImageData(0, 0, allwidth, allheight);
                //保存起始高度
                let heighttemp = 0;
                for (let i = 0; i < rowparams.length; i++) {
                    let tempwidthnum = 0;
                    for (let j = 0; j < rowparams[i].length; j++) {
                        let map = rowparams[i][j].canvas;
                        //循环小图片
                        for (let x = 0; x < map.width; x++) {
                            for (let y = 0; y < map.height; y++) {
                                //获取像素
                                let color = this.getXY(map, x, y);
                                this.setXY(editImageData, x + tempwidthnum, y + heighttemp, color);
                            }
                        }
                        spritejson += "  \"" + rowparams[i][j].name.replace("-", "/").replace("&", ":") + "\":{\"x\":";
                        spritejson += tempwidthnum + ",\"y\":" + heighttemp + ",\"width\":" + rowparams[i][j].width;
                        spritejson += ",\"height\":" + rowparams[i][j].height + ",\"pixelRatio\":1,\"sdf\":false},\n";
                        //增加宽度
                        tempwidthnum += rowparams[i][j].width;
                    }
                    heighttemp += Math.max.apply(Math, rowparams[i].map(m => m.height));
                }
                //保存大图
                editCxt.putImageData(editImageData, 0, 0);
                this.editURL = editMap.toDataURL("image/png");//取得图像的数据URI

                spritejson = spritejson.substring(0, spritejson.lastIndexOf(','));
                spritejson += "\n}";
                this.spritejson = spritejson
            },
            getXY(canvas, x, y) {
                let ctx = canvas.getContext("2d");
                // 获取画布上的图像像素矩阵
                let imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
                let w = imageData.width
                let data = imageData.data
                let color = []
                color[0] = data[(y * w + x) * 4]
                color[1] = data[(y * w + x) * 4 + 1]
                color[2] = data[(y * w + x) * 4 + 2]
                color[3] = data[(y * w + x) * 4 + 3]
                return color
            },
            setXY(imageData, x, y, color) {
                let w = imageData.width
                let data = imageData.data
                data[(y * w + x) * 4] = color[0]
                data[(y * w + x) * 4 + 1] = color[1]
                data[(y * w + x) * 4 + 2] = color[2]
                data[(y * w + x) * 4 + 3] = color[3]
                imageData.data = data;
            },
            downloadImg() {
                if (this.editURL === null || this.editURL === '') return;
                if (this.spritejson === null || this.spritejson === '') return;
                // 将图片的src属性作为URL地址
                let a = document.createElement('a')
                let event = new MouseEvent('click')
                a.download = 'sprite'
                a.href = this.editURL
                a.dispatchEvent(event);
            },
            downloadJSON(){
                let pom = document.createElement('a');
                pom.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(this.spritejson));
                pom.setAttribute('download', 'sprite.json');
                if (document.createEvent) {
                    let event = document.createEvent('MouseEvents');
                    event.initEvent('click', true, true);
                    pom.dispatchEvent(event);
                } else {
                    pom.click();
                }
            }
        }
    })
</script>
</html>
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mapbox Sprite精灵图生成 的相关文章

  • Mapbox HTML可视化点,线,多线,面带底图

    Mapbox HTML可视化点 线 多线 面带底图 1 效果图 2 源码 参考 1 效果图 如下所示 可显示蓝色点 红色线 红色多线 浅紫色多边形面 2 源码
  • vue 中 mapbox 的使用 ,(同一页面进行多次切换操作)

    一 vue 中 引入 mapbox 同一页面进行多次切换操作 1 mapbox的 底图用天地图 1准备工作 安装 mapbox mapbox gl geocoder npm install save mapbox mapbox gl geo
  • mapbox中对同一个图层layer,设置不同颜色要素

    同一图层layer的不同要素feature设置不同的颜色 InitLayer 构造函数 mapbox初始化地图 function InitLayer map this map map this jsonPoints type Feature
  • 在离线 iOS 应用程序中使用 MBTiles

    我已经有一个使用 MBTiles 格式存储的地图 我想在我正在制作的应用程序中使用它 应用程序在移动设备上没有信号 连接的情况下运行至关重要 Mapbox 之前有一个示例 介绍了如何将 MBTiles 与 RMMBTilesSource 一
  • Mapbox:libc++abi.dylib:以 NSException 类型的未捕获异常终止(lldb)

    我在 Android 上工作时一直在实现 Mapbox 通过 CocoaPods 安装后在 iOS 上遇到错误 我得到了framework not found Mapbox但在以下答案之后目标覆盖 FRAMEWORK SEARCH PATH
  • 将geojson标记加载到mapbox中设置自定义图标图像

    我是mapbox 传单的新手 我认为这是一个非常基本的问题 我在过去的两天里一直在努力解决这个问题 尽管我尝试了几种方法 但我无法解决这个问题 我通过 geojson 加载标记 var ma 3 L mapbox featureLayer
  • 在最新的 MapBox SDK 6.7 中旋转和更改标记的位置

    Mapbox Android SDK 6 7 0 我们正在开发的应用程序的要求是 我们必须在不同的 LatLng 位置添加多个标记 并使用一些方位旋转它们 在旧的mapbox版本 4 2 1 中 我们可以毫无问题地做到这一点 Working
  • 如何将 Mapbox SDK 与 SwiftUI 集成

    我将 Mapbox SDK 安装到我的项目中 但我不明白如何将此代码片段与SwiftUI 我创建了一个 SwiftUIView named MapView 我在其中导入 Mapbox 框架 我尝试使用UIViewRepresentable协
  • Mapbox - 来自 GeoJson 属性的自定义标记图标

    我正在从一组 GeoJson 对象渲染标记 我想使用自定义图标作为标记 但无法确定正确的语法 下面是我用来渲染 geojson 的部分代码 asGeoJson gt type Feature properties title get lis
  • Mapbox gl js - 如何在单个图层上添加多个照片源

    每 2 秒我创建一个新的图像源并为该源创建一个新图层 map addSource source photo id type image url photo url coordinates map addLayer id layer phot
  • Mapbox 决定重叠顺序

    I have some features on the map displayed as icons I want to decide which icons should be hidden and which should be dis
  • Mapbox GL JS 轴承

    Is it possible in Mapbox GL JS to get the users bearing I would like to show the direction in which the user is facing t
  • 自定义 Mapbox 地理编码器控件

    我觉得这对于 Google StackOverflow 搜索来说是一项简单的任务 但我似乎找不到有关该主题的任何内容 无论如何 我想做的就是创建自己的在外部工作的地理编码器搜索栏我的 Mapbox 地图 以 Zillow 主页为例 当您访问
  • 在 Mapbox iOS SDK 中将地图移动到标记下

    我是 MapBox iOS SDK 的新手 我需要在 MGLMapView 的中心添加一个标记 以便用户能够在标记下移动地图视图 并且标记将固定在屏幕上 我还需要获取地图中标记下方的点的坐标 我在 Mapbox SDK 中找不到任何方法 我
  • 如何放大 Mapbox Leaflet 中的标记单击事件?

    我想在单击标记时放大该标记 我正在使用 Mapbox 和传单 I tried marker on click function e map setView e lat e lng 12 但这给了我某种错误 类型错误 t 为空 我什至尝试过
  • MapBox水/陆检测

    我开始使用MapBox iOS SDK https www mapbox com mapbox ios sdk 有没有可能的方法通过坐标查询 MapView 并返回地形类型 水 土地 作为结果 我一直在读API doc https www
  • Flutter:无法在调试或发布中构建android

    突然我的应用程序无法在调试或发布模式下运行 我在用户界面中添加了一些屏幕 但我没有更改或添加任何包 我不知道到底发生了什么 但我尝试用 Android Studio 中的本地历史记录来逆转我的更改 但也没有发生任何事情 我的输出是 FAIL
  • Plotly Scattermapbox,无法向标记添加文本

    我正在尝试将文本添加到下面的绘图中 原始代码修改自https plotly com python lines on mapbox https plotly com python lines on mapbox import plotly g
  • 如何在 Mapbox 4.1 中添加自己的图块

    我有一个图块来源作为网址 并希望将它们添加到我的地图中 我能够做到这一点谷歌地图 and OSMDroid 但我不知道如何使用Mapbox 我的网址格式如下 http mysource x y z 我已经看到了针对网络的解决方案 但我没有找
  • Mapbox GL 中的 MaxBounds 和自定义非对称填充

    我有一个 Mapbox GL JS 应用程序 在地图上显示一些小部件 为了确保地图上的任何内容都不会被它们隐藏 我使用以下命令添加了一些填充map setPadding 这是一个不对称的 在我的例子中左边比右边大 它按预期工作 例如fitB

随机推荐

  • 命令计算机执行指定的操作,计算机如何执行一条机器指令

    计算机如何执行一条机器指令 计算机如何执行一条机器指令 文章目录指令运行过程 微程序控制基本概念 几个周期区别 寻址方式 指令运行过程 在上篇我们谈到 计算机处理一段程序 就会将程序翻译成机器指令 然后执行完成相应的任务 执行指令的过程分为
  • 为什么Python没有main函数?

    作者 豌豆花下猫 来源 Python猫 ID python cat 众所周知 Python中没有所谓的main函数 但是网上经常有文章提到 Python的main函数 和 建议编写main函数 其实 可能他们是想模仿真正的main函数 但是
  • AD18的覆铜技巧

    AD18的覆铜技巧 设置覆铜的安全距离 从工具中选择覆铜管理器 设置覆铜的安全距离 进入设计 规则 创建一个新的Clearance如下图 从工具中选择覆铜管理器 从Create NEW polygon from 中选择Board Outli
  • 37.cuBLAS开发指南中文版--cuBLAS中的Level-2函数her()

    2 6 20 cublasher cublasStatus t cublasCher cublasHandle t handle cublasFillMode t uplo int n const float alpha const cuC
  • Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available产生原因

    在项目部署时需要将代码打包放到服务器上 打包的时候报了如下的错误 但是在idea上却是能正常运行的 java lang IllegalStateException Failed to load ApplicationContext at o
  • OpenGL学习——第十课:纹理映射(1)实例

    这里使用第九课中的Texture h和Texture cpp来实现对一个正方体的六面的纹理效果 需要注意的代码就是相关纹理映射的部分 1 代码部分省去了Texture h和Texture cpp 因此运行时候需要先把这两个加入到工程目录下
  • 猿创征文

    猿创征文 国产数据库之在k8s环境下部署RadonDB MySQL集群 一 RadonDB MySQL介绍 1 RadonDB MySQL简介 2 RadonDB MySQL的应用场景 3 RadonDB MySQL核心功能 4 Radon
  • 用Python求三角形面积

    题目描述 三角形面积 SQRT S S a S b S c 其中S a b c 2 a b c为三角形的三边 定义两个带参的宏 一个用来求area 另一个宏用来求S 写程序 在程序中用带实参的宏名来求面积area 输入 a b c三角形的三
  • esp32 怎么分配freertos 堆栈大小_spiffs 文件系统在esp32中的应用

    spiffs 介绍 SPIFFS 是一个开源文件系统 用于 SPI NOR flash 设备的嵌入式文件系统 支持磨损均衡 文件系统一致性检查等功能 spiffs 源码地址 github com spiffs 特点 而我们知道乐鑫的esp3
  • Qss之QTabWidget美化

    直接上代码吧 QTabWidget QTabWidget pane border none QTabWidget tab bar left 5px QTabBar tab background gray border 2px solid C
  • 内核内存回收原理简介

    页框回收与交换 概念 内核在为进程服务的过程中会分配大量的页 但是这些页对应的虚拟地址在进程的生命周期里一直会被断断续续的访问 所以当内核同时为大量进程服务时 内存终究会耗尽 所有页框回收就是在内核未耗尽内存之前 因为回收与交换也会使用内存
  • LinkWeChat 私域管理平台基于企业微信的开源 SCRM

    LinkWeChat 是国内首个基于企业微信的开源 SCRM 在集成了企微强大的开放能力的基础上 进一步升级拓展灵活高效的客户运营能力及多元化精准营销能力 让客户与企业之间建立强链接 帮助企业提高客户运营效率 强化营销能力 拓展盈利空间 是
  • 绘制流程图的基本规则

    流程图可以简单地描述一个过程 是对过程 算法 流程的一种图像表示 在技术设计 交流及商业简报等领域有广泛的应用 流程图可分为 数据流程图和作业流程图 1 程序流程图的作用 程序流程图的作用程序流程图的作用程序流程图的作用 程序流程图是人们对
  • 优秀个人技术博客

    优秀个人技术博客 作为一个程序员 我们经常需要去网络上寻找大量的参考资料以解决当前遇到的问题 就应用型需求问题几乎都不可避免的会被许多不同地域不同时间的开发者遇到 这类问题可以直接通过前人的解决方案来进行自我消化病指导解决我们遇到的问题 但
  • CSS让DIV上下左右居中的方法

    示例 一个父div w 100 h 400px 中有一个子div w 100px h 100px 让其上下左右居中 1 使用varticle align属性 理念 利用表格单元格的居中属性 步骤 1 父div外层配置一个div 同时设置为表
  • 音频属性相关:声道、采样率、采样位数、样本格式、比特率

    不仅限于FFmpeg 音频采样所得的PCM都含有三个要素 声道 channel 采样率 sample rate 样本格式 sample rate 声道 当人听到声音时 能对声源进行定位 那么通过在不同的位置设置声源 就可以造就出更好的听觉感
  • MPEG压缩编码的视频基本流 - MATLAB实现

    MPEG压缩编码的视频基本流 MATLAB实现 视频压缩是一种广泛应用于数字媒体领域的技术 它可以有效地减少视频文件的大小 同时保持较高的视觉质量 其中 MPEG Moving Picture Experts Group 压缩编码标准是一种
  • web前端技术笔记(一)html简介及常用标签、页面布局

    今天是星期二 h1 一级标题 h1 h2 二级标题 h2 h3 三级标题 h3 h4 四级标 h4
  • 嵌入式课程结业总结

    我大学学的专业就是计算机 之前就接触过c c 等计算机语言 在大学期间 上课的方式就是老师在讲台上讲 学生在书本上划重点 做笔记等 很少有实操的机会 学的东西基本都停留在理论状态 独立敲代码的能力基本没有 来到华清之后 刚开始的预科C的课程
  • Mapbox Sprite精灵图生成

    出处 ATtuing 博客园 https www cnblogs com ATtuing p 9273391 html 1 什么是sprite文件 sprite 文件主要是将一堆小图生成一种大图的方法 并且将每张小图的位置信息保存下来 方便