数据可视化【原创】vue+arcgis+threejs 实现流光立体墙效果
本文适合对vue,arcgis4.x,threejs,ES6较熟悉的人群食用。
效果图:

素材:


主要思路:
先用arcgis externalRenderers封装了一个ExternalRendererLayer,在里面把arcgis和threejs的context关联,然后再写个子类继承它,这部分类容在上一个帖子里面有讲过。
子类AreaLayer继承它,并在里面实现绘制流光边界墙的方法,这里用的BufferGeometry构建几何对象,材质是ShaderMaterial着色器。关键点就在于下面这2个方法。
1:创建材质ShaderMaterial createWallMaterial
1 /**
2 * 创建流体墙体材质
3 * option =>
4 * params bgUrl flowUrl
5 * **/
6 const createWallMaterial = ({
7 bgTexture,
8 flowTexture
9 }) => {
10 // 顶点着色器
11 const vertexShader = `
12 varying vec2 vUv;
13 varying vec3 fNormal;
14 varying vec3 vPosition;
15 void main(){
16 vUv = uv;
17 vPosition = position;
18 vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
19 gl_Position = projectionMatrix * mvPosition;
20 }
21 `;
22 // 片元着色器
23 const fragmentShader = `
24 uniform float time;
25 varying vec2 vUv;
26 uniform sampler2D flowTexture;
27 uniform sampler2D bgTexture;
28 void main( void ) {
29 vec2 position = vUv;
30 vec4 colora = texture2D( flowTexture, vec2( vUv.x, fract(vUv.y - time )));
31 vec4 colorb = texture2D( bgTexture , position.xy);
32 gl_FragColor = colorb + colorb * colora;
33 }
34 `;
35 // 允许平铺
36 flowTexture.wrapS = THREE.RepeatWrapping;
37 return new THREE.ShaderMaterial({
38 uniforms: {
39 time: {
40 value: 0,
41 },
42 flowTexture: {
43 value: flowTexture,
44 },
45 bgTexture: {
46 value: bgTexture,
47 },
48 },
49 transparent: true,
50 depthWrite: false,
51 depthTest: false,
52 side: THREE.DoubleSide,
53 vertexShader: vertexShader,
54 fragmentShader: fragmentShader,
55 });
56 };
2:创建BufferGeometry createWallByPath
1 /**
2 * 通过path构建墙体
3 * option =>
4 * params height path material expand(是否需要扩展路径)
5 * **/
6 export const createWallByPath = ({
7 height = 10,
8 path = [],
9 material,
10 expand = true,
11 }) => {
12 let verticesByTwo = null;
13 // 1.处理路径数据 每两个顶点为为一组
14 if (expand) {
15 // 1.1向y方向拉伸顶点
16 verticesByTwo = path.reduce((arr, [x, y, z]) => {
17 return arr.concat([
18 [
19 [x, y, z],
20 [x, y, z + height],
21 ],
22 ]);
23 }, []);
24 } else {
25 // 1.2 已经处理好路径数据
26 verticesByTwo = path;
27 }
28 // 2.解析需要渲染的四边形 每4个顶点为一组
29 const verticesByFour = verticesByTwo.reduce((arr, item, i) => {
30 if (i === verticesByTwo.length - 1) return arr;
31 return arr.concat([
32 [item, verticesByTwo[i + 1]]
33 ]);
34 }, []);
35 // 3.将四边形面转换为需要渲染的三顶点面
36 const verticesByThree = verticesByFour.reduce((arr, item) => {
37 const [
38 [point1, point2],
39 [point3, point4]
40 ] = item;
41 return arr.concat(
42 ...point2,
43 ...point1,
44 ...point4,
45 ...point1,
46 ...point3,
47 ...point4
48 );
49 }, []);
50 const geometry = new THREE.BufferGeometry();
51 // 4. 设置position
52 const vertices = new Float32Array(verticesByThree);
53 geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
54 // 5. 设置uv 6个点为一个周期 [0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1]
55
56 // 5.1 以18个顶点为单位分组
57 const pointsGroupBy18 = new Array(verticesByThree.length / 3 / 6)
58 .fill(0)
59 .map((item, i) => {
60 return verticesByThree.slice(i * 3 * 6, (i + 1) * 3 * 6);
61 });
62 // 5.2 按uv周期分组
63 const pointsGroupBy63 = pointsGroupBy18.map((item, i) => {
64 return new Array(item.length / 3)
65 .fill(0)
66 .map((it, i) => item.slice(i * 3, (i + 1) * 3));
67 });
68 // 5.3根据BoundingBox确定uv平铺范围
69 geometry.computeBoundingBox();
70 const {
71 min,
72 max
73 } = geometry.boundingBox;
74 const rangeX = max.x - min.x;
75 const uvs = [].concat(
76 ...pointsGroupBy63.map((item) => {
77 const point0 = item[0];
78 const point5 = item[5];
79 const distance =
80 new THREE.Vector3(...point0).distanceTo(new THREE.Vector3(...point5)) /
81 (rangeX / 10);
82 return [0, 1, 0, 0, distance, 1, 0, 0, distance, 0, distance, 1];
83 })
84 );
85 geometry.setAttribute(
86 "uv",
87 new THREE.BufferAttribute(new Float32Array(uvs), 2)
88 );
89 const meshMat =
90 material ||
91 new THREE.MeshBasicMaterial({
92 color: 0x00ffff,
93 side: THREE.DoubleSide,
94 });
95 return new THREE.Mesh(geometry, meshMat);
96 };
3:最后再updateModels里面更新贴图的位置(其实就是render事件)
1 updateModels(context) {
2 super.updateModels(context);
3
4 this.objects.forEach(obj => {
5 obj.material.uniforms.time.value += 0.01;
6 })
7 }
ExternalRendererLayer:

1 import * as THREE from 'three'
2 import Stats from 'three/examples/jsm/libs/stats.module.js'
3 import * as webMercatorUtils from "@arcgis/core/geometry/support/webMercatorUtils"
4 import * as externalRenderers from "@arcgis/core/views/3d/externalRenderers"
5
6 export default class ExternalRendererLayer {
7 constructor({
8 view,
9 options
10 }) {
11 this.view = view
12 this.options = options
13
14 this.objects = []
15 this.scene = null
16 this.camera = null
17 this.renderer = null
18
19 this.setup();
20 }
21
22 setup() {
23 if (process.env.NODE_ENV !== "production") {
24 const sid = setTimeout(() => {
25 clearTimeout(sid)
26 //构建帧率查看器
27 let stats = new Stats()
28 stats.setMode(0)
29 stats.domElement.style.position = 'absolute'
30 stats.domElement.style.left = '0px'
31 stats.domElement.style.top = '0px'
32 document.body.appendChild(stats.domElement)
33 function render() {
34 stats.update()
35 requestAnimationFrame(render)
36 }
37 render()
38 }, 5000)
39 }
40 }
41
42 apply() {
43 let myExternalRenderer = {
44 setup: context => {
45 this.createSetup(context)
46 },
47 render: context => {
48 this.createRender(context)
49 }
50 }
51
52 externalRenderers.add(this.view, myExternalRenderer);
53 }
54
55 createSetup(context) {
56 this.scene = new THREE.Scene(); // 场景
57 this.camera = new THREE.PerspectiveCamera(); // 相机
58
59 this.setLight();
60
61 // 添加坐标轴辅助工具
62 const axesHelper = new THREE.AxesHelper(10000000);
63 this.scene.Helpers = axesHelper;
64 this.scene.add(axesHelper);
65
66 this.renderer = new THREE.WebGLRenderer({
67 context: context.gl, // 可用于将渲染器附加到已有的渲染环境(RenderingContext)中
68 premultipliedAlpha: false, // renderer是否假设颜色有 premultiplied alpha. 默认为true
69 // antialias: true
70 // logarithmicDepthBuffer: false
71 // logarithmicDepthBuffer: true
72 });
73 this.renderer.setPixelRatio(window.devicePixelRatio); // 设置设备像素比。通常用于避免HiDPI设备上绘图模糊
74 this.renderer.setViewport(0, 0, this.view.width, this.view.height); // 视口大小设置
75
76 // 防止Three.js清除ArcGIS JS API提供的缓冲区。
77 this.renderer.autoClearDepth = false; // 定义renderer是否清除深度缓存
78 this.renderer.autoClearStencil = false; // 定义renderer是否清除模板缓存
79 this.renderer.autoClearColor = false; // 定义renderer是否清除颜色缓存
80 // this.renderer.autoClear = false;
81
82 // ArcGIS JS API渲染自定义离屏缓冲区,而不是默认的帧缓冲区。
83 // 我们必须将这段代码注入到three.js运行时中,以便绑定这些缓冲区而不是默认的缓冲区。
84 const originalSetRenderTarget = this.renderer.setRenderTarget.bind(
85 this.renderer
86 );
87 this.renderer.setRenderTarget = target => {
88 originalSetRenderTarget(target);
89 if (target == null) {
90 // 绑定外部渲染器应该渲染到的颜色和深度缓冲区
91 context.bindRenderTarget();
92 }
93 };
94
95 this.addModels(context);
96
97 context.resetWebGLState();
98 }
99
100 createRender(context) {
101 const cam = context.camera;
102 this.camera.position.set(cam.eye[0], cam.eye[1], cam.eye[2]);
103 this.camera.up.set(cam.up[0], cam.up[1], cam.up[2]);
104 this.camera.lookAt(
105 new THREE.Vector3(cam.center[0], cam.center[1], cam.center[2])
106 );
107 // this.camera.near = 1;
108 // this.camera.far = 100;
109
110 // 投影矩阵可以直接复制
111 this.camera.projectionMatrix.fromArray(cam.projectionMatrix);
112
113 this.updateModels(context);
114
115 this.renderer.state.reset();
116
117 context.bindRenderTarget();
118
119 this.renderer.render(this.scene, this.camera);
120
121 // 请求重绘视图。
122 externalRenderers.requestRender(this.view);
123
124 // cleanup
125 context.resetWebGLState();
126 }
127
128 //经纬度坐标转成三维空间坐标
129 lngLatToXY(view, points) {
130
131 let vector3List; // 顶点数组
132
133 let pointXYs;
134
135
136 // 计算顶点
137 let transform = new THREE.Matrix4(); // 变换矩阵
138 let transformation = new Array(16);
139
140 // 将经纬度坐标转换为xy值\
141 let pointXY = webMercatorUtils.lngLatToXY(points[0], points[1]);
142
143 // 先转换高度为0的点
144 transform.fromArray(
145 externalRenderers.renderCoordinateTransformAt(
146 view,
147 [pointXY[0], pointXY[1], points[
148 2]], // 坐标在地面上的点[x值, y值, 高度值]
149 view.spatialReference,
150 transformation
151 )
152 );
153
154 pointXYs = pointXY;
155
156 vector3List =
157 new THREE.Vector3(
158 transform.elements[12],
159 transform.elements[13],
160 transform.elements[14]
161 )
162
163 return {
164 vector3List: vector3List,
165 pointXYs: pointXYs
166 };
167 }
168
169 setLight() {
170 console.log('setLight')
171 let ambient = new THREE.AmbientLight(0xffffff, 0.7);
172 this.scene.add(ambient);
173 let directionalLight = new THREE.DirectionalLight(0xffffff, 0.7);
174 directionalLight.position.set(100, 300, 200);
175 this.scene.add(directionalLight);
176 }
177
178 addModels(context) {
179 console.log('addModels')
180 }
181
182 updateModels(context) {
183 // console.log('updateModels')
184 }
185
186 }
AreaLayer:源码中mapx.queryTask是封装了arcgis的query查询,这个可以替换掉,我只是要接收返回的rings数组,自行构建静态数据也行

1 import mapx from '@/utils/mapUtils.js';
2 import * as THREE from 'three'
3 import ExternalRendererLayer from './ExternalRendererLayer.js'
4 import Graphic from "@arcgis/core/Graphic";
5 import SpatialReference from '@arcgis/core/geometry/SpatialReference'
6 import * as externalRenderers from "@arcgis/core/views/3d/externalRenderers"
7
8 const WALL_HEIGHT = 200;
9
10 export default class ArealLayer extends ExternalRendererLayer {
11 constructor({
12 view,
13 options
14 }) {
15 super({
16 view,
17 options
18 })
19 }
20
21 addModels(context) {
22 super.addModels(context);
23 // let pointList = [
24 // [114.31456780904838, 30.55355011036358],
25 // [114.30888002358996, 30.553227103422344],
26 // [114.31056780904838, 30.56355011036358],
27 // [114.31256780904838, 30.58355011036358]
28 // ];
29
30 const url = config.mapservice[1].base_url + config.mapservice[1].jd_url;
31 // const url = 'http://10.100.0.132:6080/arcgis/rest/services/wuchang_gim/gim_region/MapServer/2';
32 mapx.queryTask(url, {
33 where: '1=1',
34 returnGeometry: true
35 }).then(featureSet => {
36 if (featureSet.length > 0) {
37 featureSet.forEach(feature => {
38 const polygon = feature.geometry;
39 const rings = polygon.rings;
40 rings.forEach(ring => {
41 this._addModel(ring);
42 })
43 })
44 }
45 }).catch(error => {
46 console.log(error)
47 })
48 }
49
50 updateModels(context) {
51 super.updateModels(context);
52
53 this.objects.forEach(obj => {
54 obj.material.uniforms.time.value += 0.01;
55 })
56 }
57
58 _addModel(pointList) {
59 // =====================mesh加载=================================//
60 let linePoints = [];
61
62 //确定几何体位置
63 pointList.forEach((item) => {
64 var renderLinePoints = this.lngLatToXY(this.view, [item[0], item[1], 0]);
65 linePoints.push(new THREE.Vector3(renderLinePoints.vector3List.x, renderLinePoints
66 .vector3List.y, renderLinePoints.vector3List.z));
67 })
68
69 // "https://model.3dmomoda.com/models/47007127aaf1489fb54fa816a15551cd/0/gltf/116802027AC38C3EFC940622BC1632BA.jpg"
70 const bgImg = require('../../../../public/static/img/b9a06c0329c3b4366b972632c94e1e8.png');
71 const bgTexture = new THREE.TextureLoader().load(bgImg);
72 const flowImg = require('../../../../public/static/img/F3E2E977BDB335778301D9A1FA4A4415.png');
73 const flowTexture = new THREE.TextureLoader().load(flowImg);
74 const material = createWallMaterial({
75 bgTexture,
76 flowTexture
77 });
78 const wallMesh = createWallByPath({
79 height: WALL_HEIGHT,
80 path: linePoints,
81 material,
82 expand: true
83 });
84 this.scene.add(wallMesh);
85 this.objects.push(wallMesh);
86 }
87 }
88
89 /**
90 * 创建流体墙体材质
91 * option =>
92 * params bgUrl flowUrl
93 * **/
94 const createWallMaterial = ({
95 bgTexture,
96 flowTexture
97 }) => {
98 // 顶点着色器
99 const vertexShader = `
100 varying vec2 vUv;
101 varying vec3 fNormal;
102 varying vec3 vPosition;
103 void main(){
104 vUv = uv;
105 vPosition = position;
106 vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
107 gl_Position = projectionMatrix * mvPosition;
108 }
109 `;
110 // 片元着色器
111 const fragmentShader = `
112 uniform float time;
113 varying vec2 vUv;
114 uniform sampler2D flowTexture;
115 uniform sampler2D bgTexture;
116 void main( void ) {
117 vec2 position = vUv;
118 vec4 colora = texture2D( flowTexture, vec2( vUv.x, fract(vUv.y - time )));
119 vec4 colorb = texture2D( bgTexture , position.xy);
120 gl_FragColor = colorb + colorb * colora;
121 }
122 `;
123 // 允许平铺
124 flowTexture.wrapS = THREE.RepeatWrapping;
125 return new THREE.ShaderMaterial({
126 uniforms: {
127 time: {
128 value: 0,
129 },
130 flowTexture: {
131 value: flowTexture,
132 },
133 bgTexture: {
134 value: bgTexture,
135 },
136 },
137 transparent: true,
138 depthWrite: false,
139 depthTest: false,
140 side: THREE.DoubleSide,
141 vertexShader: vertexShader,
142 fragmentShader: fragmentShader,
143 });
144 };
145
146
147 /**
148 * 通过path构建墙体
149 * option =>
150 * params height path material expand(是否需要扩展路径)
151 * **/
152 export const createWallByPath = ({
153 height = 10,
154 path = [],
155 material,
156 expand = true,
157 }) => {
158 let verticesByTwo = null;
159 // 1.处理路径数据 每两个顶点为为一组
160 if (expand) {
161 // 1.1向y方向拉伸顶点
162 verticesByTwo = path.reduce((arr, [x, y, z]) => {
163 return arr.concat([
164 [
165 [x, y, z],
166 [x, y, z + height],
167 ],
168 ]);
169 }, []);
170 } else {
171 // 1.2 已经处理好路径数据
172 verticesByTwo = path;
173 }
174 // 2.解析需要渲染的四边形 每4个顶点为一组
175 const verticesByFour = verticesByTwo.reduce((arr, item, i) => {
176 if (i === verticesByTwo.length - 1) return arr;
177 return arr.concat([
178 [item, verticesByTwo[i + 1]]
179 ]);
180 }, []);
181 // 3.将四边形面转换为需要渲染的三顶点面
182 const verticesByThree = verticesByFour.reduce((arr, item) => {
183 const [
184 [point1, point2],
185 [point3, point4]
186 ] = item;
187 return arr.concat(
188 ...point2,
189 ...point1,
190 ...point4,
191 ...point1,
192 ...point3,
193 ...point4
194 );
195 }, []);
196 const geometry = new THREE.BufferGeometry();
197 // 4. 设置position
198 const vertices = new Float32Array(verticesByThree);
199 geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
200 // 5. 设置uv 6个点为一个周期 [0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1]
201
202 // 5.1 以18个顶点为单位分组
203 const pointsGroupBy18 = new Array(verticesByThree.length / 3 / 6)
204 .fill(0)
205 .map((item, i) => {
206 return verticesByThree.slice(i * 3 * 6, (i + 1) * 3 * 6);
207 });
208 // 5.2 按uv周期分组
209 const pointsGroupBy63 = pointsGroupBy18.map((item, i) => {
210 return new Array(item.length / 3)
211 .fill(0)
212 .map((it, i) => item.slice(i * 3, (i + 1) * 3));
213 });
214 // 5.3根据BoundingBox确定uv平铺范围
215 geometry.computeBoundingBox();
216 const {
217 min,
218 max
219 } = geometry.boundingBox;
220 const rangeX = max.x - min.x;
221 const uvs = [].concat(
222 ...pointsGroupBy63.map((item) => {
223 const point0 = item[0];
224 const point5 = item[5];
225 const distance =
226 new THREE.Vector3(...point0).distanceTo(new THREE.Vector3(...point5)) /
227 (rangeX / 10);
228 return [0, 1, 0, 0, distance, 1, 0, 0, distance, 0, distance, 1];
229 })
230 );
231 geometry.setAttribute(
232 "uv",
233 new THREE.BufferAttribute(new Float32Array(uvs), 2)
234 );
235 const meshMat =
236 material ||
237 new THREE.MeshBasicMaterial({
238 color: 0x00ffff,
239 side: THREE.DoubleSide,
240 });
241 return new THREE.Mesh(geometry, meshMat);
242 };
数据可视化【原创】vue+arcgis+threejs 实现流光立体墙效果的更多相关文章
- 数据可视化 seaborn绘图(1)
seaborn是基于matplotlib的数据可视化库.提供更高层的抽象接口.绘图效果也更好. 用seaborn探索数据分布 绘制单变量分布 绘制二变量分布 成对的数据关系可视化 绘制单变量分布 se ...
- 基于vue和echarts的数据可视化实现
基于vue和echarts的数据可视化: https://github.com/MengFangui/awesome-vue.git
- [原创.数据可视化系列之五]韩国"萨德"系统防御图
自从7月8日美国和韩国共同宣布将在韩国部署萨德反导系统后,韩国国内对此事的巨大争议以及本地区一些国家的强烈不满情绪在持续发酵.“萨德”(THAAD)全称“末段高空区域防御系统”,是美国导弹防御局和美国 ...
- [原创.数据可视化系列之八]使用等d3进行灰度图转伪彩色
对灰度图进行彩色化是数据可视化中常见的需求,使用d3在客户端比较容易实现,本文使用d3生成图片,并显示: 代码如下: 代码中首先下载数据文件,然后设定d3的色带信息,生成一个空白的canvas元素,并 ...
- 地理数据可视化:Simple,Not Easy
如果要给2015年的地理信息行业打一个标签,地理大数据一定是其中之一.在信息技术飞速发展的今天,“大数据”作为一种潮流铺天盖地的席卷了各行各业,从央视的春运迁徙图到旅游热点预测,从大数据工程师奇货可居 ...
- D3js初探及数据可视化案例设计实战
摘要:本文以本人目前所做项目为基础,从设计的角度探讨数据可视化的设计的方法.过程和结果,起抛砖引玉之效.在技术方案上,我们采用通用web架构和d3js作为主要技术手段:考虑到项目需求,这里所做的可视化 ...
- 【Data Visual】一文搞懂matplotlib数据可视化
一文搞懂matplotlib数据可视化 作者:白宁超 2017年7月19日09:09:07 摘要:数据可视化主要旨在借助于图形化手段,清晰有效地传达与沟通信息.但是,这并不就意味着数据可视化就一定因为 ...
- Python数据可视化的四种简易方法
摘要: 本文讲述了热图.二维密度图.蜘蛛图.树形图这四种Python数据可视化方法. 数据可视化是任何数据科学或机器学习项目的一个重要组成部分.人们常常会从探索数据分析(EDA)开始,来深入了解数据, ...
- 倾斜摄影数据OSGB进入到ArcGIS平台相关问题小结
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/zglybl/article/details/75252288 随着倾斜摄影技术的发展,大家 ...
- 【Matplotlib】数据可视化实例分析
数据可视化实例分析 作者:白宁超 2017年7月19日09:09:07 摘要:数据可视化主要旨在借助于图形化手段,清晰有效地传达与沟通信息.但是,这并不就意味着数据可视化就一定因为要实现其功能用途而令 ...
随机推荐
- ODOO13之12:Odoo 13开发之报表和服务端 QWeb
报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...
- windows内核学习一
变量类型 kernel user ULONG unsigned long PULONG unsigned long* UCHAR unsigned char PUCHAR unsigned char* ...
- Java发展史、JDK环境配置、运行原理及开发工具(学习的前期准备)
一.Java的历史和三大版本 1.Java的发展史 Java由sun公司开发,Java之父James Gosling,Java是一门面向对象的编程语言,也称为"高级编程语言" Ja ...
- WPF中有中心点的slider滑动条
想要实现的效果 原生滑动条 需要认识一下滑动条的组成 在原生控件中生成"资源字典"对应的样式 然后在track所在的列进行添砖加瓦 由于track在row="1" ...
- 一分钟学一个 Linux 命令 - cat 和 tail
前言 大家好,我是 god23bin.今天我给大家带来的是 Linux 命令系列,每天只需一分钟,记住一个 Linux 命令不成问题.今天,需要你花费两分钟时间,因为我们要介绍的是两个常用的查看文件内 ...
- Spring Boot实现高质量的CRUD-5
(续前文) 9.Service实现类代码示例 以用户管理模块为例,展示Service实现类代码.用户管理的Service实现类为UserManServiceImpl.UserManServi ...
- 用声明式宏解析 Rust 语法之 enum parser
上一篇用声明式宏解析 Rust 语法 我们的 "macro parser" 解析了 function 和 struct, 这篇来尝试 parse 一下更复杂的 enum 为什么说 ...
- @SafeVarargs注解的使用
在声明具有模糊类型(比如:泛型)的可变参数的构造函数或方法时,Java编译器会报unchecked警告.鉴于这些情况,如果程序员断定声明的构造函数和方法的主体不会对其varargs参数执行潜在的不安全 ...
- RDD练习:词频统计
一.词频统计: 1.读文本文件生成RDD lines lines=sc.textFile("file:///home/hadoop/word.txt") #读取本地文件 lines ...
- 把jar包打成docker镜像并推送到Docker Hub
1.准备需要的jar包并复制到服务器某个目录下 2.在此目录下,创建Dockerfile的文本文件,并将以下内容添加到文件中: # 基础镜像 FROM openjdk:8-jre # author(可 ...