Skip to content

Commit

Permalink
feat: 优化生成时的UI提示逻辑
Browse files Browse the repository at this point in the history
  • Loading branch information
shenlvmeng committed Sep 9, 2018
1 parent 27adb7b commit ec7a74a
Show file tree
Hide file tree
Showing 4 changed files with 659 additions and 67 deletions.
4 changes: 2 additions & 2 deletions app/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="utf-8">
<title>Traces - 你的运动轨迹整合工具</title>
<style>
*{margin:0;padding:0}body,html{height:100%}body{display:flex;justify-content:center;align-items:center;background:#fdfdfd;font-family:"Helvetica Neue","Microsoft Yahei",Arial,sans-serif}.wrapper div{width:350px;margin:30px auto}p{text-align:center;}label{margin-right:10px;font-weight:100}input{font:14px/2 "Helvetica Neue","Microsoft Yahei",Arial,sans-serif}input[type=text]{width:240px;padding:0 5px;border:1px solid #aaa;border-radius:2px;color:#333}input[type=file]{display:inline-flex;font-weight:100;outline:0}#upload{width:120px;margin-bottom:10px;font-size:16px;line-height:2;text-align:center;color:#fff;background-color:#4bb505;border-radius:4px;cursor:pointer}label.must:before{content: '*';margin-left: -6px;color: #f00;}.ak-link{font-size:12px;margin-left:8px;}.issue-link{font-size:12px;color:#aaa}
*{margin:0;padding:0}body,html{height:100%}body{display:flex;justify-content:center;align-items:center;background:#fdfdfd;font-family:"Helvetica Neue","Microsoft Yahei",Arial,sans-serif}.wrapper div{width:350px;margin:30px auto}p{text-align:center;}label{margin-right:10px;font-weight:100}input{font:14px/2 "Helvetica Neue","Microsoft Yahei",Arial,sans-serif}input[type=text]{width:240px;padding:0 5px;border:1px solid #aaa;border-radius:2px;color:#333}input[type=file]{display:inline-flex;font-weight:100;outline:0}#upload{width:120px;margin-bottom:10px;font-size:16px;line-height:2;text-align:center;color:#fff;background-color:#4bb505;border-radius:4px;cursor:pointer}label.must:before{content: '*';margin-left: -6px;color: #f00;}.ak-link{font-size:12px;margin-left:8px;}.issue-link{font-size:12px;color:#aaa}#upload.loading{color: #fff;background-color: #aaa;cursor: not-allowed;}
</style>
</head>
<body>
Expand All @@ -25,7 +25,7 @@ <h1>Traces</h1>
require('./renderer.js')
</script>
<script type="text/template" id="template">
<!DOCTYPE html>&lt;html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"><style>.locations,div.panel{color:#fff;position:absolute}.hide .curr,.locations.hide{opacity:0}.close,.locations,div.panel{position:absolute}.city,.close{cursor:pointer}#map,body,html{width:100%;height:100%;overflow:hidden;margin:0}div.panel{top:10px;left:20px;width:210px;font:lighter 14px/1.8 'Helvetica Neue','Microsoft Yahei',Arial,sans-serif}.panel div{background-color:rgba(63,81,181,.5);margin-bottom:10px;padding-left:5px;border-radius:2px}.panel .curr{background-color:rgba(244,67,54,.5);transition:opacity 1s ease-in-out}.locations{bottom:20px;left:50%;min-width:320px;padding:10px 20px;border-radius:2px;transform:translateX(-50%);font-size:14px;font-weight:lighter;background-color:rgba(63,81,181,.5);box-sizing:border-box;z-index:100;transition:opacity ease-in-out .5s}.locations>div{line-height:1.5}.locations span{line-height:1}.city{text-decoration:underline}.close{right:5px;color:#fdfdfd;top:5px;font-size:18px}</style><title><%=title%></title>&lt;/head>&lt;body><div id="map"></div><div class="panel hide" id="panel"><div>总里程:<span id="distance"></span> km</div><div>总时间:<span id="time"></span></div><div class="curr">当次里程:<span id="curr_distance"></span> km</div><div class="curr">运动时间:<span id="curr_time"></span></div><div class="curr">出发时间:<span id="start_time"></span></div></div><div class="locations">我去过的地方<div class="province">省份:<span id="provinces"></span></div><div class="cities">城市:<span id="cities"></span></div><span class="close" id="close">×</span></div></body>&lt;script src="https://api.map.baidu.com/api?v=2.0&ak=<%=key%>">&lt;/script>&lt;script>!function(){var e=0,n=0,t=null,a=Array.apply(null,Array(<%=length%>)).map(function(e,n){return n+".json"}),i=[],o=[];function r(e){return document.getElementById(e)}function c(e){var n=e.city,t=e.province;t&&-1===i.indexOf(t)&&i.push(t),n&&-1===o.indexOf(n)&&o.push(n),r("provinces").innerText=i.map(function(e){return e.slice(0,-1)}).join(", "),r("cities").innerHTML=o.map(function(e){return"<span class='city'>"+e.slice(0,-1)+"</span>"}).join(", ")}function s(e){return e>31536e3?~~(e/315636e3)+" "+s(e%31536e3):e>2592e3?~~(e/2592e3)+" "+s(e%2592e3):e>86400?~~(e/86400)+" "+s(e%86400):e>3600?~~(e/3600)+"h "+s(e%3600):e>60?~~(e/60)+"m "+s(e%60):e.toFixed(0)+"s"}function d(){t&&(t.setStrokeColor("#3a6bdb"),r("panel").className+=" hide",t=null)}var l=new BMap.Map("map");l.centerAndZoom('<%=city%>'),l.addControl(new BMap.MapTypeControl({mapTypes:[BMAP_NORMAL_MAP,BMAP_HYBRID_MAP]})),l.enableScrollWheelZoom(!0),l.addEventListener("click",function(e){d()}),r("cities").addEventListener("click",function(e){(e.target.className="city")&&l.centerAndZoom(e.target.innerText)}),r("close").addEventListener("click",function(e){e.target.parentNode.className+=" hide"}),function i(){var o,p,u;a.length&&(o=a.shift(),p=function(a){a&&i();try{var o=JSON.parse(a);e+=+o.distance,n+=+o.time;var p=o.points.map(function(e){return new BMap.Point(e.lng,e.lat)}),u=new BMap.Polyline(p);u.metadata={distance:o.distance,time:o.time,startTime:o.startTime},u.addEventListener("click",function(e){d(),e.target.setStrokeColor("#f44336");var n=e.target.metadata;r("curr_distance").innerText=n.distance,r("curr_time").innerText=s(n.time),r("start_time").innerText=n.startTime,r("panel").className="panel",t=e.target,e.domEvent.stopPropagation()}),l.addOverlay(u),(new BMap.Geocoder).getLocation(p[0],function(e){c(e.addressComponents)}),(new BMap.Geocoder).getLocation(p[~~(p.length/2)],function(e){c(e.addressComponents)}),(new BMap.Geocoder).getLocation(p[p.length-1],function(e){c(e.addressComponents)}),r("distance").innerText=e.toFixed(3),r("time").innerText=s(n)}catch(e){console.warn(e)}},(u=new XMLHttpRequest).open("GET",o,!0),u.onreadystatechange=function(){4===u.readyState&&200===u.status&&p(u.response)},u.send())}()}();
<!DOCTYPE html>&lt;html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"><style>.locations,div.panel{color:#fff;position:absolute}.hide .curr,.locations.hide{display:none;}.close,.locations,div.panel{position:absolute}.city,.close{cursor:pointer}#map,body,html{width:100%;height:100%;overflow:hidden;margin:0}div.panel{top:10px;left:20px;width:210px;font:lighter 14px/1.8 'Helvetica Neue','Microsoft Yahei',Arial,sans-serif}.panel div{background-color:rgba(63,81,181,.5);margin-bottom:10px;padding-left:5px;border-radius:2px}.panel .curr{background-color:rgba(244,67,54,.5);transition:opacity 1s ease-in-out}.locations{bottom:20px;left:50%;min-width:320px;padding:10px 20px;border-radius:2px;transform:translateX(-50%);font-size:14px;font-weight:lighter;background-color:rgba(63,81,181,.5);box-sizing:border-box;z-index:100;transition:opacity ease-in-out .5s}.locations>div{line-height:1.5}.locations span{line-height:1}.city{text-decoration:underline}.close{right:5px;color:#fdfdfd;top:5px;font-size:18px}.panel .copyright{background: inherit;color: #9a9a9a;font-weight: 400;font-size: 12px;}</style><title><%=title%></title>&lt;/head>&lt;body><div id="map"></div><div class="panel hide" id="panel"><div>总里程:<span id="distance"></span> km</div><div>总时间:<span id="time"></span></div><div class="curr">当次里程:<span id="curr_distance"></span> km</div><div class="curr">运动时间:<span id="curr_time"></span></div><div class="curr">出发时间:<span id="start_time"></span></div><div class="copyright">Made with ❤ by shenlvmeng</div></div><div class="locations">我去过的地方<div class="province">省份:<span id="provinces"></span></div><div class="cities">城市:<span id="cities"></span></div><span class="close" id="close">×</span></div></body>&lt;script src="https://api.map.baidu.com/api?v=2.0&ak=<%=key%>">&lt;/script>&lt;script>!function(){var e=0,n=0,t=null,a=Array.apply(null,Array(<%=length%>)).map(function(e,n){return n+".json"}),i=[],o=[];function r(e){return document.getElementById(e)}function c(e){var n=e.city,t=e.province;t&&-1===i.indexOf(t)&&i.push(t),n&&-1===o.indexOf(n)&&o.push(n),r("provinces").innerText=i.map(function(e){return e.slice(0,-1)}).join(", "),r("cities").innerHTML=o.map(function(e){return"<span class='city'>"+e.slice(0,-1)+"</span>"}).join(", ")}function s(e){return e>31536e3?~~(e/315636e3)+" "+s(e%31536e3):e>2592e3?~~(e/2592e3)+" "+s(e%2592e3):e>86400?~~(e/86400)+" "+s(e%86400):e>3600?~~(e/3600)+"h "+s(e%3600):e>60?~~(e/60)+"m "+s(e%60):e.toFixed(0)+"s"}function d(){t&&(t.setStrokeColor("#3a6bdb"),r("panel").className+=" hide",t=null)}var l=new BMap.Map("map");l.centerAndZoom('<%=city%>'),l.addControl(new BMap.MapTypeControl({mapTypes:[BMAP_NORMAL_MAP,BMAP_HYBRID_MAP]})),l.enableScrollWheelZoom(!0),l.addEventListener("click",function(e){d()}),r("cities").addEventListener("click",function(e){(e.target.className="city")&&l.centerAndZoom(e.target.innerText)}),r("close").addEventListener("click",function(e){e.target.parentNode.className+=" hide"}),function i(){var o,p,u;a.length&&(o=a.shift(),p=function(a){a&&i();try{var o=JSON.parse(a);e+=+o.distance,n+=+o.time;var p=o.points.map(function(e){return new BMap.Point(e.lng,e.lat)}),u=new BMap.Polyline(p);u.metadata={distance:o.distance,time:o.time,startTime:o.startTime},u.addEventListener("click",function(e){d(),e.target.setStrokeColor("#f44336");var n=e.target.metadata;r("curr_distance").innerText=n.distance,r("curr_time").innerText=s(n.time),r("start_time").innerText=n.startTime,r("panel").className="panel",t=e.target,e.domEvent.stopPropagation()}),l.addOverlay(u),(new BMap.Geocoder).getLocation(p[0],function(e){c(e.addressComponents)}),(new BMap.Geocoder).getLocation(p[~~(p.length/2)],function(e){c(e.addressComponents)}),(new BMap.Geocoder).getLocation(p[p.length-1],function(e){c(e.addressComponents)}),r("distance").innerText=e.toFixed(3),r("time").innerText=s(n)}catch(e){console.warn(e)}},(u=new XMLHttpRequest).open("GET",o,!0),u.onreadystatechange=function(){4===u.readyState&&200===u.status&&p(u.response)},u.send())}()}();
&lt;/script>
&lt;/html>
</script>
Expand Down
156 changes: 92 additions & 64 deletions app/renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,87 +8,115 @@ const { wgs2bd } = require('./gps')
const { tmpl } = require('./utils')

const OUTPUT_PATH = path.join(require('os').homedir(), 'Desktop', 'traces-output')
const uploadBtn = document.querySelector('#upload')
let hasFinished = false
let isGenerating = false

function gps2points(data) {
const track = data.tracks
const distance = track.reduce((acc, cur) => acc + cur.length(), 0).toFixed(3)
const startTime = new Date(+data.metadata.time - 480 * 60 * 1000).toLocaleString()
const flattenTrack = track.reduce((acc, cur) => (cur.segments.reduce((acc, cur) => acc.concat(cur), []).concat(acc)), [])
const time = (+flattenTrack[flattenTrack.length - 1].time - +data.metadata.time) / 1000
const points = flattenTrack.map(wgs2bd).map(({lat, lon}) => ({lat, lng: lon}))
return { points, distance, startTime, time }
function activate() {
isGenerating = false;
uploadBtn.innerText = 'Generate';
uploadBtn.classList.remove('loading');
}

function serialize(file, index) {
gpxParse.parseGpxFromFile(file.path, function(error, data) {
if (error || !data.tracks) {
alert('文件内容错误')
return
}
const gpsData = gps2points(data)
try {
const jsonData = JSON.stringify(gpsData)
const pathStr = path.join(OUTPUT_PATH, `${index}.json`)
remote.require('fs').writeFile(pathStr, jsonData,'utf8', err => {
if (err) throw err
})
} catch (e) {
console.warn(e)
alert('文件序列化失败')
}
});
function deactivate() {
isGenerating = true;
uploadBtn.innerText = 'Generating…';
uploadBtn.classList.add('loading');
}

document.getElementById('upload').addEventListener('click', () => {
const fileList = document.getElementById('files').files
const key = document.getElementById('ak').value.trim()
const title = document.getElementById('title').value.trim() || "我的骑行记录"
const city = document.getElementById('city').value.trim() || "北京"

localStorage.setItem('traces', JSON.stringify({city, title, ak: key}))
function gps2points(data) {
const track = data.tracks
const distance = track.reduce((acc, cur) => acc + cur.length(), 0).toFixed(3)
const startTime = new Date(+data.metadata.time - 480 * 60 * 1000).toLocaleString()
const flattenTrack = track.reduce((acc, cur) => (cur.segments.reduce((acc, cur) => acc.concat(cur), []).concat(acc)), [])
const time = (+flattenTrack[flattenTrack.length - 1].time - +data.metadata.time) / 1000
const points = flattenTrack.map(wgs2bd).map(({lat, lon}) => ({lat, lng: lon}))
return { points, distance, startTime, time }
}

if (!key) {
alert('您忘记填写ak秘钥了')
return
}
function serialize(file, index, arr) {
gpxParse.parseGpxFromFile(file.path, function(error, data) {
if (error || !data.tracks) {
alert('文件内容错误')
activate();
return
}
const gpsData = gps2points(data)
try {
const jsonData = JSON.stringify(gpsData)
const pathStr = path.join(OUTPUT_PATH, `${index}.json`)
remote.require('fs').writeFile(pathStr, jsonData, 'utf8', err => {
if (err) throw err
remote.require('fs').readdir(OUTPUT_PATH, (err, files) => {
if (files.length > arr.length && !hasFinished) {
hasFinished = true
activate();
alert('生成完毕!\n将output文件夹下所有文件上传到服务器即可查看效果!')
}
})
})
} catch (e) {
console.warn(e)
activate();
alert('文件序列化失败')
}
})
}

remote.require('fs').mkdir(OUTPUT_PATH, 0o777, err => {
if (err) {
alert('文件夹创建失败:\n文件夹已存在或权限不足')
return
}
// 生成模板HTML文件
const template = document.getElementById('template').innerHTML
const data = {
title,
length: fileList.length || 0,
key,
city
document.getElementById('upload').addEventListener('click', function() {
if (isGenerating) {
return
}
if (!data.length) {
if (!confirm('并未上传gpx数据,是否继续?\n点击“取消”返回上传数据,点击“确认”继续操作。')) {
deactivate();

const fileList = document.getElementById('files').files
const key = document.getElementById('ak').value.trim()
const title = document.getElementById('title').value.trim() || "我的骑行记录"
const city = document.getElementById('city').value.trim() || "北京"
localStorage.setItem('traces', JSON.stringify({city, title, ak: key}))

if (!key) {
alert('您忘记填写ak秘钥了')
return
}
}
Array.from(fileList).forEach(serialize)
remote.require('fs').writeFile(path.join(OUTPUT_PATH, 'index.html'), tmpl(template)(data).replace(/&lt;/g, '<'),'utf8', err => {
if (err) throw err
else alert('生成完毕!\n将output文件夹下所有文件上传到服务器即可查看效果!')

remote.require('fs').mkdir(OUTPUT_PATH, 0o777, err => {
if (err) {
alert('文件夹创建失败:\n文件夹已存在或权限不足')
activate();
return
}
// 生成模板HTML文件
const template = document.getElementById('template').innerHTML
const data = {
title,
length: fileList.length || 0,
key,
city
}
if (!data.length) {
if (!confirm('并未上传gpx数据,是否继续?\n点击“取消”返回上传数据,点击“确认”继续操作。')) {
return
}
}
Array.from(fileList).forEach(serialize)
remote.require('fs').writeFile(path.join(OUTPUT_PATH, 'index.html'), tmpl(template)(data).replace(/&lt;/g, '<'),'utf8', err => {
if (err) throw err
})
})
})
})

// 取消默认的a标签行为
document.querySelectorAll('a[href^="http"]').forEach(node => {
node.addEventListener('click', function(event) {
event.preventDefault()
shell.openExternal(event.target.href)
});
node.addEventListener('click', function(event) {
event.preventDefault()
shell.openExternal(event.target.href)
});
})

const presets = JSON.parse(localStorage.getItem('traces'))
Object.keys(presets).forEach(key => {
if (presets[key]) {
document.getElementById(key).value = presets[key]
}
if (presets[key]) {
document.getElementById(key).value = presets[key]
}
})
Loading

0 comments on commit ec7a74a

Please sign in to comment.