项目截图
要点
开发模式监听端口8888
修改保存文件的同时,浏览器实时刷新。
css文件、三方js库文件,业务js文件分别打包。
生产环境和开发环境
google map API 3相关功能
- 本地生产环境 run build之后,开启mac自带服务器,设置apachectl服务器默认路径为打包输出目录,模拟生产环境访问。
=======
首先,确保已全局安装node webpack webpack-dev-server
查看是是否安装,若没安装,请自行google 安装
node -v
webpack -v
webpack-dev-server -v
1.安装相应模块
npm install autoprefixer babel-core babel-loader babel-plugin-transform-runtime babel-preset-es2015 clean-webpack-plugin css-loader exports-loader extract-text-webpack-plugin html-webpack-plugin imports-loader postcss-loader style-loader stylus stylus-loader webpack webpack-dev-server html-withimg-loader url-loader
2.配置webpack.config.js
开发环境:
// 该配置基于webpack2.0 详情查看 https://webpack.js.org/guides/migrating/
const path = require('path'); // 导入路径包
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
devtool: 'eval-source-map', //开启sourceMap便于调试
entry: './src/main.js', //入口文件
output: {
path: path.resolve(__dirname, 'build'), // 指定打包之后的文件夹
// publicPath: '/assets/', //指定资源文件引用的目录
// filename: 'bundle.js' // 指定打包为一个文件 bundle.js
filename: '[name].js' // 可以打包为多个文件
},
// 使用loader模块
module: {
/* 在webpack2.0版本已经将 module.loaders 改为 module.rules 为了兼容性考虑以前的声明方法任然可用,同时链式loader(用!连接)只适用于module.loader
同时-loader不可省略 */
rules: [{
test: /\.css$/,
use: [
'style-loader', {
loader: 'css-loader',
options: {
// modules: true // 设置css模块化,详情参考https://github.com/css-modules/css-modules
}
}, {
loader: 'postcss-loader',
// 在这里进行配置,也可以在postcss.config.js中进行配置,详情参考https://github.com/postcss/postcss-loader
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
}
]
}, {
test: /\.styl(us)?$/,
use: [
'style-loader', 'css-loader', {
loader: "postcss-loader",
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
}, 'stylus-loader'
]
}, {
test: /\.(png|jpg|gif)$/,
use: [
'url-loader', {
loader: 'url-loader?limit=81920000&name=images/[hash:8].[name].[ext]',
options: {
// modules: true // 设置css模块化,详情参考https://github.com/css-modules/css-modules
}
}
]
}, {
test: /\.html$/,
use: [
'html-withimg-loader', {
loader: 'html-withimg-loader'
}
]
}, {
test: /\.js$/,
loader: "babel-loader",
include: /src/,
exclude: /node_modules/
}]
},
// 配置devServer各种参数
devServer: {
// contentBase: "./", // 本地服务器所加载的页面所在的目录
port: 8888,
hot: true, // 配置HMR之后可以选择开启
historyApiFallback: true, // 不跳转
inline: true // 实时刷新
},
plugins: [
new HtmlWebpackPlugin({
template: './sail-index.html' // 模版文件
}),
new webpack.HotModuleReplacementPlugin(), // 热加载插件
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
}
生产环境:
// 该配置基于webpack2.0 详情查看 https://webpack.js.org/guides/migrating/
const path = require('path'); // 导入路径包
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var CleanPlugin = require('clean-webpack-plugin')//webpack插件,用于清除目录文件
module.exports = {
entry: './src/main.js', //入口文件
output: {
path: path.resolve(__dirname, 'build'), // 指定打包之后的文件夹
// publicPath: '/assets/', //指定资源文件引用的目录
// filename: 'bundle.js' // 指定打包为一个文件 bundle.js
filename: '[name]-[hash].js' // 可以打包为多个文件
},
// 使用loader模块
module: {
/* 在webpack2.0版本已经将 module.loaders 改为 module.rules 为了兼容性考虑以前的声明方法任然可用,同时链式loader(用!连接)只适用于module.loader
同时-loader不可省略 */
rules: [{
test: /\.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [{
loader: 'css-loader',
options: {
// modules: true // 设置css模块化,详情参考https://github.com/css-modules/css-modules
}
}, {
loader: 'postcss-loader',
// 在这里进行配置,也可以在postcss.config.js中进行配置,详情参考https://github.com/postcss/postcss-loader
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
}
]
})
}, {
test: /\.styl(us)?$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: ['css-loader', {
loader: "postcss-loader",
options: {
plugins: function () {
return [
require('autoprefixer')
];
}
}
}, 'stylus-loader']
})
}, {
test: /\.(png|jpg|gif)$/,
use: [
'url-loader', {
loader: 'url-loader?limit=81920000&name=images/[hash:8].[name].[ext]',
options: {
// modules: true // 设置css模块化,详情参考https://github.com/css-modules/css-modules
}
}
]
}, {
test: /\.html$/,
use: [
'html-withimg-loader', {
loader: 'html-withimg-loader',
options: {
// modules: true // 设置css模块化,详情参考https://github.com/css-modules/css-modules
}
}
]
}, {
test: /\.js$/,
loader: 'babel-loader', //此处不能用use,不知道为啥
exclude: /node_modules/ //需要排除的目录
}]
},
plugins: [
new HtmlWebpackPlugin({
// template: './sail-index.html' // 模版文件
template: './index.html', // 模版文件
inject: true, //允许插件修改哪些内容,true/'head'/'body'/false,
hash: false, //为静态资源生成hash值
minify: { //压缩HTML文件
removeComments: true, //移除HTML中的注释
collapseWhitespace: true //删除空白符与换行符
}
}),
new CleanPlugin(['build']),
// new webpack.optimize.CommonsChunkPlugin({
// name: 'vendors' // 将公共模块提取,生成名为`vendors`的chunk
// }),
new webpack.optimize.UglifyJsPlugin({ //压缩js代码
compress: {
warnings: false
}
}),
new ExtractTextPlugin('[name]-[hash].css')
]
}
或者,新建package.josn文件
{
"name": "sail-map",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"start": "webpack-dev-server",
"build": "set NODE_ENV=production&&webpack --config ./webpack.production.config.js"
},
"author": "安静的猫",
"license": "ISC",
"devDependencies": {
"autoprefixer": "^6.7.3",
"babel-core": "^6.23.1",
"babel-loader": "^6.3.2",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-es2015": "^6.22.0",
"clean-webpack-plugin": "^0.1.15",
"css-loader": "^0.26.1",
"exports-loader": "^0.6.4",
"extract-text-webpack-plugin": "^2.0.0-beta.5",
"html-webpack-plugin": "^2.28.0",
"imports-loader": "^0.7.1",
"postcss-loader": "^1.3.0",
"style-loader": "^0.13.1",
"stylus": "^0.54.5",
"stylus-loader": "^2.4.0",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.3.0"
},
"dependencies": {
"html-withimg-loader": "^0.1.16",
"url-loader": "^0.5.9"
}
}
然后,在项目根目录运行
npm install
运行
开发环境
npm run start
生产环境
npm run build
Google Map API 相关功能
-
引入地图
-
地图初始化
function InitMap() { this.map = null; } InitMap.prototype.init = function (dom) { var _this = this; var map = new google.maps.Map(document.getElementById(dom), { center: { lat: 30.279626, lng: 119.9934259 }, zoom: 15, disableDefaultUI: true,//是否显示控件——所有 // zoomControl: false,//是否显示控件——单个 // streetViewControl: false, // mapTypeControl: false, mapDataProviders: '123', mapTypeId: 'satellite',//地图类型 // mapTypeId:google.maps.MapTypeId.SATELLITE, // HYBRID 显示一种带公路和城市名称的照相地图。 // ROADMAP 显示一种标准的,默认是 2D 的地图。 // SATELLITE 显示一种照相地图。 // TERRAIN 显示一种带有山脉、河流等的地图。 mapTypeControlOptions: {//控件位置 position: google.maps.ControlPosition.LEFT_BOTTOM, style: google.maps.MapTypeControlStyle.HORIZONTAL_BAR } }); InitMap.map = map; }; export {InitMap}
注意:本项目采用装逼ES6引入模块方式
-
地图点标记——自定义Marker
//引入自定义图标 import {usericon, OriginSrc} from './js/base64Image' var marker1 = new google.maps.Marker({ map: map, draggable: false,//是否可拖动 position: UserLocation,//图标地图上位置 title: 'usericon', icon: { size: new google.maps.Size(24, 24),//自定义图标icon大小 anchor: new google.maps.Point(12, 12),//自定义图标icon锚点 origin: new google.maps.Point(0, 0),//自定义图标iconw中心点 scaledSize: new google.maps.Size(24, 24),//自定义图标icon缩放大小,不设置,则采用默认大小 url: usericon//自定义图标icon } });
-
获取特定点的位置信息(Geocoder)
/***** * 获取位置名称信息 */ var geocoder = new google.maps.Geocoder(); function geocodeAddress(elevator) { geocoder.geocode({'location': OriginLocation}, function (results, status) { if (status === 'OK') { console.log('results is :', results, ' formatted_address is :', results[0].formatted_address) } else { return status; alert('Geocode was not successful for the following reason: ' + status); } }); }
-
屏幕像素位置(x,y)转换为地图坐标
/***** * 屏幕像素位置(x,y)转换为地图坐标 * @param point * @param map * @returns {*} */ function point2LatLng(point, map) { var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast()); var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest()); var scale = Math.pow(2, map.getZoom()); var worldPoint = new google.maps.Point(point.x / scale + bottomLeft.x, point.y / scale + topRight.y); var result = map.getProjection().fromPointToLatLng(worldPoint); return result; }
参考链接:https://stackoverflow.com/questions/25219346/how-to-convert-from-x-y-screen-coordinates-to-latlng-google-maps
结合像素位置转换和点标记可做一个类似滴滴、共享单车,图钉不动,拖动地图,更新图钉坐标的效果。
-
给定偏移值,屏幕上像素值,x,y等偏移地图中心
function offsetCenter(latlng, offsetx, offsety) { /* latlng is the apparent centre-point offsetx is the distance you want that point to move to the right, in pixels offsety is the distance you want that point to move upwards, in pixels offset can be negative*/ var scale = Math.pow(2, map.getZoom()); var nw = new google.maps.LatLng( map.getBounds().getNorthEast().lat(), map.getBounds().getSouthWest().lng() ); var oldCenter = latlng; var worldCoordinateCenter = map.getProjection().fromLatLngToPoint(latlng); var pixelOffset = new google.maps.Point((offsetx / scale) || 0, (offsety / scale) || 0); var worldCoordinateNewCenter = new google.maps.Point( worldCoordinateCenter.x - pixelOffset.x, worldCoordinateCenter.y + pixelOffset.y ); var newCenter = map.getProjection().fromPointToLatLng(worldCoordinateNewCenter); map.setCenter(newCenter); }
参考链接:https://stackoverflow.com/questions/10656743/how-to-offset-the-center-point-in-google-maps-api-v3/10666030#10666030
-
自定义图层(也可以这种方式自定义marker)
CustomMarker.prototype = new google.maps.OverlayView(); /** @constructor */ function CustomMarker(bounds, image, map, args) { this.bounds_ = bounds; this.image_ = image; this.map_ = map; this.div_ = null; this.args = args; this.setMap(map); } /** * onAdd is called when the map's panes are ready and the overlay has been * added to the map. */ CustomMarker.prototype.onAdd = function () { var deg = this.args.deg; var div = document.createElement('div');//可在此设置你需要的标签和样式 div.style.borderStyle = 'dashed'; div.style.borderWidth = '2px'; div.style.borderColor = '#77BAFF'; div.style.position = 'absolute'; div.style.backgroundColor = 'rgba(96,191,254,0.1)'; // Create the img element and attach it to the div. // var img = document.createElement('img'); // img.src = this.image_; // img.style.width = '100%'; // img.style.height = '100%'; // img.style.position = 'absolute'; // div.appendChild(img); this.div_ = div; var panes = this.getPanes(); panes.overlayLayer.appendChild(div); div.style.transform = 'rotate(' + deg + 'deg)'; div.style.transformOrigin = '0 100%'; }; CustomMarker.prototype.draw = function () { var deg = this.args.deg; var overlayProjection = this.getProjection(); var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest()); var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast()); // Resize the image's div to fit the indicated dimensions. var div = this.div_; div.style.left = sw.x + 'px'; div.style.top = ne.y + 'px'; div.style.width = (ne.x - sw.x) + 'px'; div.style.height = (sw.y - ne.y) + 'px'; }; CustomMarker.prototype.onRemove = function () { this.div_.parentNode.removeChild(this.div_); this.div_ = null; }; export {CustomMarker}
-
测海拔(google.maps.ElevationService)
——注意,测海拔功能有使用次数限制,每天可免费使用2500次,若有较大使用量可考虑购买相应套餐。/***** * 获取海拔数据 * @type {google.maps.ElevationService} */ var elevator = new google.maps.ElevationService; function displayLocationElevation(elevator) { elevator.getElevationForLocations({ 'locations': [OriginLocation] }, function (results, status) { if (status === 'OK') { if (results[0]) { console.log('elevation is :', results[0].elevation + ' meters.'); } else { return ''; console.log('No results found'); } } else { return status; console.log('Elevation service failed due to: ' + status); } }); }
参考链接:https://developers.google.com/maps/documentation/javascript/elevation
-
画矩形,可旋转、可设置大小(m) ,并且可获得旋转之后矩形四个角经纬度
这个比较复杂,下面一步一步来。 主要有以下几个知识点:
1.自定义图层new google.maps.OverlayView();
2.根据距离和航向画矩形 google.maps.geometry.spherical.computeOffset(Origin, Distance, Angle);
3.旋转矩形 div.style.transform = 'rotate(' + deg + 'deg)'; div.style.transformOrigin = '0 100%';
4.视图自适应缩放 map.fitBounds(pointBounds);
下面是核心代码
/***** * 根据给定的边长、角度在地图上画矩形 * overlayLayer * @param args */ function drawRectangle(args) { var dis, angle, center, curValueY; if (args.distance) { dis = args.distance; angle = args.angle; center = args.position; curValueY = args.ValueY; console.log('center is :', center) } if (!dis || dis === '') { } else { Distance = dis; } if ((!angle || angle === '') && angle !== 0) { } else { Angle = angle; } if (!center || center === '') { center = LNGLAT; } if (!curValueY || curValueY === '') { } else { ValueY = curValueY; } if (overlay) { overlay.setMap(null); overlay = null; Origin = null; Destination = null; bounds = null; } var atanAngle = Math.atan2(Distance, ValueY) * 180 / Math.PI;//默认角度是弧度,要转为google map需要的角度 Origin = new google.maps.LatLng(OriginLocation.lat, OriginLocation.lng); // var Destination = google.maps.geometry.spherical.computeOffset(Origin, Math.sqrt(dis * dis), Angle); var PointLat = google.maps.geometry.spherical.computeOffset(Origin, ValueY, Angle); var PointLnglat = google.maps.geometry.spherical.computeOffset(Origin, Math.sqrt(ValueY * ValueY + Distance * Distance), atanAngle + Angle); var PointLng = google.maps.geometry.spherical.computeOffset(Origin, Distance, 90 + Angle); var overlayLnglat = google.maps.geometry.spherical.computeOffset(Origin, Math.sqrt(ValueY * ValueY + Distance * Distance), atanAngle); points = []; points.push({lng: Origin.lng(), lat: Origin.lat(), v: 2, alt: 100}); points.push({lng: PointLng.lng(), lat: PointLng.lat(), v: 2, alt: 100}); points.push({lng: PointLnglat.lng(), lat: PointLnglat.lat(), v: 2, alt: 100}); points.push({lng: PointLat.lng(), lat: PointLat.lat(), v: 2, alt: 100}); // console.log('Origin is :', Origin.lat(), Origin.lng(), 'PointLat is :', PointLat.lat(), PointLat.lng(), 'PointLnglat is :', PointLnglat.lat(), PointLnglat.lng(), 'PointLng is :', PointLng.lat(), PointLng.lng()); if (Angle < -45 && Angle > -180) { var pointBounds = new google.maps.LatLngBounds( new google.maps.LatLng(PointLnglat.lat(), PointLnglat.lng()), new google.maps.LatLng(PointLng.lat(), PointLng.lng()), ); } else if (Angle == -180 || Angle > 135) { var pointBounds = new google.maps.LatLngBounds( new google.maps.LatLng(PointLng.lat(), PointLng.lng()), new google.maps.LatLng(PointLat.lat(), PointLat.lng()), ); } else { var pointBounds = new google.maps.LatLngBounds( new google.maps.LatLng(Origin.lat(), Origin.lng()), new google.maps.LatLng(PointLnglat.lat(), PointLnglat.lng()), new google.maps.LatLng(PointLng.lat(), PointLng.lng()), new google.maps.LatLng(PointLat.lat(), PointLat.lng()), ); } var overlayBounds = new google.maps.LatLngBounds( new google.maps.LatLng(Origin.lat(), Origin.lng()), new google.maps.LatLng(overlayLnglat.lat(), overlayLnglat.lng()), ); overlay = new CustomMarker(overlayBounds, RectangleSrc, map, {'deg': Angle}); map.fitBounds(pointBounds);//可视化地图最合理视图——此处蛋疼,会默认以旋转之前的坐标进行可视化 => 直角三角形求斜边边长算错啦! }
参考链接:
https://developers.google.com/maps/documentation/javascript/geometry
http://www.svennerberg.com/2011/04/calculating-distances-and-areas-in-google-maps-api-3/https://developers.google.com/maps/documentation/javascript/examples/overlay-remove
https://developers.google.com/maps/documentation/javascript/examples/overlay-hideshow
https://developers.google.com/maps/documentation/javascript/customoverlays#add
https://developers.google.com/maps/documentation/javascript/examples/overlay-simple