暖色调

Every little helps


  • 首页

  • 归档

  • 分类

  • 标签

  • 关于

  • 搜索
close

基于wepy框架的微信小程序开发

发表于 2018-10-10   |   分类于 前端技术   |  

wepy-housekeeper

微信小程序–你的个人生活管家

本项目基于wepy框架搭建了用于记录生活的微信小程序,使用了微信运动、天气、图片轮播、redux数据管理、云函数云数据库及云存储等特性。首先简要介绍原生微信语法及wepy框架,然后依次对以上使用到的技术进行说明。

一、微信小程序原生语法

1、应用结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
project
├── pages
| ├── index
| | ├── index.js index 页面逻辑
| | ├── index.json index 页面配置
| | ├── index.wxml index 页面结构
| | └── index.wxss index 页面样式
| └── log
| ├── log.js log 页面逻辑
| ├── log.json log 页面配置
| ├── log.wxml log 页面结构
| └── log.wxss log 页面样式
├── app.js 小程序逻辑
├── app.json 小程序公共配置
└── app.wxss 小程序公共样式
2、循环列表
1
2
3
4
<view wx:for="{{array}}" wx:for-index="idx" wx:for-item="itemName"
wx:key="unique">
{{idx}}: {{itemName.message}}
</view>

wx:for-item 可以指定数组当前元素的变量名,默认值:item

wx:for-index 可以指定数组当前下标的变量名,默认值:index

wx:key 代表唯一值的属性,如果item是数字,使用 wx:key=”*this”

3、条件渲染
1
2
3
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
4、事件
1
<view id="inner" bind:touchstart="handleTap3" capture-bind:touchstart="handleTap4"></view>

bind:(冒泡阶段)
catch:(冒泡阶段)将取消冒泡阶段;
capture-bind:(捕获阶段)
capture-catch:(捕获阶段)将中断捕获阶段和取消冒泡阶段。

5、引用
1
2
<import src="item.wxml"/>
<include src="header.wxml"/>

import:只会 import 目标文件中定义的 template
include:将目标文件代码拷贝到include位置

6、数据绑定

this.setData({title: ‘this is title’});

二、wepy框架简介

1、应用结构
1
2
3
4
5
6
project
└── src
├── pages
| ├── index.wpy index 页面逻辑、配置、结构、样式
| └── log.wpy log 页面逻辑、配置、结构、样式
└──app.wpy 小程序逻辑、公共配置、公共样式
2、app.wpy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<script>
import wepy from 'wepy';
export default class extends wepy.app {
config = {
"pages":[
"pages/index/index"
],
"window":{
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "WeChat",
"navigationBarTextStyle": "black"
},
"tabBar": {
"list": [] //导航栏
}
};
onLaunch() {
console.log(this);
}
globalData = {} //全局数据
}
</script>

<style lang="less">
/** less **/
</style>
3、page.wpy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<script>
import wepy from 'wepy';
import Counter from '../components/counter'; //自定义组件

export default class Page extends wepy.page {
config = {}; // 页面的配置项
components = {counter1: Counter};

data = {}; // 用于渲染的数据,template中直接使用,script中this.xx
computed = {}; // 声明计算属性
watch = {}; // 声明数据watcher
methods = {};// wxml事件处理函数对象

events = {}; // 用于监听组件之间的通信与交互事件的事件处理函数
onLoad() {}; // 生命周期函数
// Other properties / 普通自定义方法 / 自定义数据
}
</script>

<template lang="wxml">
<view>
</view>
<counter1></counter1>
</template>

<style lang="less">
/** less **/
</style>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意点:
1、this.$parent来访问App实例
2、循环
<!-- 注意,使用for属性,而不是使用wx:for属性 -->
<repeat for="{{list}}" key="index" index="index" item="item">
</repeat>

3、事件绑定语法
bindtap="click" => @tap="click"
catchtap="click" => @tap.stop="click"
capture-bind:tap="click" => @tap.capture="click"
capture-catch:tap="click" => @tap.capture.stop="click"

4、数据绑定
this.title = 'this is title';
this.$apply();

三、项目解析

image

1、微信运动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
1. 登录
async getUserInfo(cb) {
if (this.globalData.userInfo) {
return this.globalData.userInfo;
}
const login = await wepy.login();
const res = await wepy.getUserInfo();
const userInfo = {...login, ...res.userInfo};
this.globalData.userInfo = userInfo;
cb && cb(userInfo);
}

2. 获取微信运动数据(加密数据)
let result = await wepy.getWeRunData();
3. 解密数据
3.1 调用https://api.weixin.qq.com/sns/jscode2session 接口获取openid及session_key
3.2 调用云函数获取到解密后的微信步数
wx.cloud.callFunction({
name: 'getRunData', // 云函数名称
data: { // 传给云函数的参数
encryptedData: data.encryptedData,
iv: data.iv,
session_key: res.data.session_key
}
});
3.3 云函数
const cloud = require('wx-server-sdk')
const WXBizDataCrypt = require('WXBizDataCrypt.js');
const AppId = 'wxf6c91f679a67b89c';
cloud.init();
// 云函数入口函数
exports.main = async (event, context) => {
var pc = new WXBizDataCrypt(AppId, event.session_key);
return pc.decryptData(event.encryptedData, event.iv);
};
4. 获取最后一天的步数
const stepList = res.result.stepInfoList;
const last = stepList[stepList.length - 1];
this.lastDayStep = last.step;
2、定位及地理编码
1
2
3
4
5
6
1、定位,得到经纬度
const res = await wepy.getLocation();
const latitude = res.latitude;//纬度
const longitude = res.longitude;//经度
2、调用百度地图API进行地理编码
https://api.map.baidu.com/geocoder/v2/
3、图片轮播
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1、图片上传到云存储管理
2、将图片地址列表放到easyMock里
3、请求数据
let result = await wepy.request(this.$parent.globalData.apiHost + '/banner/list');
let urls = await wx.cloud.getTempFileURL({
fileList: result.data.data
});
this.banners = urls.fileList;
4、加载图片
<swiper
class="swiper_box"
autoplay="{{autoplay}}"
interval="{{interval}}"
duration="{{duration}}"
@change="swiperchange"
>
<repeat for="{{banners}}" key="fileID">
<swiper-item>
<image
src="{{item.tempFileURL}}"
class="slide-image"
width="750rpx"
height="400rpx"
/>
</swiper-item>
</repeat>
</swiper>
4、天气、redux–组建间数据共享
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
1、reducer

export default handleActions({
[GET_WEATHER] (state, action) {
const data = action.payload.data.HeWeather5[0];
const weather = data.now;
const weatherQuality = data.aqi;
return {
...state,
weather,
weatherQuality
}
}
}, {
weather: {},
weatherQuality: {}
});

2、action

export const getWeather = createAction(GET_WEATHER, async city => {
const url = "https://free-api.heweather.com/v5/weather";
return await wepy.request(`${url}?city=${city}&key=4a555d4d1adc451d8ceeaa73869c9519`);
});

3、组件内使用
import { connect } from 'wepy-redux';

@connect({
weather(state) {
return state.weather.weather;
},
weatherQuality(state) {
return state.weather.weatherQuality;
}
},
{
getWeather,
getOpenId
})
// 使用state xxx
// 使用action this.methods.xxx()
5、写日记(云函数云数据库及云存储)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
1、选择位置
const res = await wepy.chooseLocation();
this.address = res.address;
this.longitude = res.longitude;
this.latitude = res.latitude;
2、日期选择器
<picker class="item-content" mode="date" value="{{date}}" @change="bindDateChange">
<view class="weui-input">{{date}}</view>
</picker>
3、使用redux获取天气数据
4、图片选择及预览
async chooseImage() {
const res = await wepy.chooseImage({
sourceType: ['camera', 'album'],
sizeType: ['compressed', 'original'],
count: 9
});
this.imageList = res.tempFilePaths;
},
previewImage(e) {
const current = e.target.dataset.src;
wx.previewImage({
current,
urls: this.imageList
});
}
5、提交数据(云存数、云函数、云数据库)
const requsets = this.imageList.map(tempFilePath => wepy.saveFile({tempFilePath})); // 云存储
Promise.all(requsets).then(res => {
const imgs = u.pluck(res, 'savedFilePath');
const data = {...};
wx.cloud.callFunction({ // 云函数
name: 'addDiary',
data
}).then(() => this.resetFormData());
});

// 云函数中的内容
cloud.init();
const db = cloud.database();
exports.main = async (event, context) => {
try {
return await db.collection('diary').add({
data: {// data 字段表示需新增的 JSON 数据}
})
}
catch(e) {
console.error(e)
}
}

image

6、查看日记(云函数云数据库及云存储)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1、分页加载数据
onReachBottom() { // 拉到页面最低端触发,请求下一页数据
this.curPage += 1;
this.getDiaryList(true);
}

onPullDownRefresh() { // 拉到页面最上面触发,刷新数据
this.curPage = 1;
this.getDiaryList();
}
2、获取数据列表(通过云函数)
cloud.init();
const db = cloud.database()
exports.main = async (event, context) => {
const skipNum = parseInt((event.page - 1) * event.pageSize);
const limitNum = parseInt(event.pageSize);
return await db.collection('diary')
.where({
userid: event.userid
})
.skip(skipNum)
.limit(limitNum)
.get();
};

image

短信服务

发表于 2018-10-10   |   分类于 后台   |  

方法一:阿里大于API,需要企业账号登录。

alibaba.aliqin.ta.sms.num.send (短信发送)

第一步:注册账号。

第二步:创建短信签名和短信模板。

第三步:创建应用。

第四步:下载SDK。

第五步:调用接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
header("Content-type: text/html; charset=utf-8");
include "TopSdk.php";
include "top/TopClient.php";
date_default_timezone_set('Asia/Shanghai');

$c = new TopClient;
$c->appkey = "23270934";
$c->secretKey = "******";
$req = new AlibabaAliqinFcSmsNumSendRequest;
$req->setExtend("123");
$req->setSmsType("normal");
$req->setSmsFreeSignName("三林镇建设项目");
$req->setSmsParam('{"product":"xiaoming","code":"987"}');
$req->setRecNum("13262208625");
$req->setSmsTemplateCode("SMS_2570065");
$resp = $c->execute($req);

方法二:阿里百川API。

http://baichuan.taobao.com/doc2/detail?spm=0.0.0.0.o6kDjh&treeId=42&articleId=103193&docType=1
taobao.open.sms.sendmsg (发送短信)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
header("Content-type: text/html; charset=utf-8");
include "TopSdk.php";
include "top/TopClient.php";
date_default_timezone_set('Asia/Shanghai');

$callback = isset($_GET['callback']) ? trim($_GET['callback']) : '';
$name = isset($_GET['name']) ? trim($_GET['name']) : 'mm';
$con = isset($_GET['con']) ? trim($_GET['con']) : 'wuuu';
$mobile_num = isset($_GET['mobile_num']) ? trim($_GET['mobile_num']) : '13262208625';

$c = new TopClient;
$c->appkey = "23270857";
$c->secretKey = "******";
$req = new OpenSmsSendmsgRequest;
$send_message_request = new SendMessageRequest;
$send_message_request->template_id="879";
$send_message_request->signature_id="795";
$send_message_request->context=json_decode("{\"name\":\"".$name."\",\"con\":\"".$con."\"}");
$send_message_request->mobile=$mobile_num;

$req->setSendMessageRequest(json_encode($send_message_request));
$resp = $c->execute($req);

$date = 'success';
$tmp= json_encode($date); //json 数据
echo $callback . '(' . $tmp .')'; //返回格式,必需

现代web前端开发工具和流程

发表于 2018-10-10   |   分类于 前端技术   |  

1.版本控制

  • SVN
  • GIT
    推荐使用git,git安装和图形化界面tortoiseGit安装,git与github联系不在本文的讨论范围,请自行搜索。
    在github中新建一个项目
    在本地使用图形Git–>git clone
    或者使用命令:
    1
    git clone  git://github.com/someone/some_project.git

文件夹就是我们的项目文件夹。

2.前端自动化

  • gulp
  • grunt

2.1 gulp

Gulp通过流和代码优于配置策略来尽量简化任务编写的工作。当使用流时,Gulp去除了中间文件,只将最后的输出写入磁盘,整个过程因此变得更快。

2.1.1 安装命令行工具

1
npm install -g gulp

2.1.2 创建好package.json文件(包管理文件),安装局部gulp

1
npm install --save-dev gulp

下载的包会存放在项目的node_modules文件夹下
包依赖会加入到package.json中:

1
2
3
"devDependencies": {
"gulp": "^3.9.1"
}

2.1.3 新建gulpfile.js文件

四个主要API

  • gulp.task(name[, deps], fn):注册任务
  • gulp.src(globs[, options]):指明源文件路径
  • gulp.dest(path):指明任务处理后的目标输出路径
  • gulp.watch(glob[, options], tasks)/gulp.watch(glob[, options, cb]):监视文件的变化并运行相应的任务。

实例见3.2 在gulpfile.js中配置。

2.1.4 运行

1
gulp

2.2 grunt

2.2.1 安装 grunt-cli

1
npm install grunt-cli -g

2.2.2 配置grunt

在项目中添加两份文件:package.json 和 Gruntfile。

  • package.json //项目自动化所依赖的相关插件。
  • Gruntfile.js //项目自动化工作流配置文件。

安装Grunt 和 grunt插件

1
2
npm install grunt --save-dev
npm install grunt-contrib-jshint --save-dev

配置Gruntfile.js文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
});

// 加载包含 "uglify" 任务的插件。
grunt.loadNpmTasks('grunt-contrib-uglify');

// 默认被执行的任务列表。
grunt.registerTask('default', ['uglify']);
};

2.2.3 运行

1
grunt

3.SASS

‘CSS预处理器’,它的基本思想是,用一种专门的编程语言,进行网页样式设计,然后再编译成正常的CSS文件。

3.1 安装

1
2
npm install --save-dev gulp-sass
npm install --save-dev gulp-watch

gulp-sass是用来将SASS转化为CSS的,gulp-watch是用来观察文件修改的变化
我们来看package.json文件的变化

1
2
3
4
5
"devDependencies": {
"gulp":"^3.9.1",
"gulp-sass":"^2.3.1",
"gulp-watch":"^4.3.6",
}

3.2 在gulpfile.js 中配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
'use strict';
var gulp = require('gulp');
var sass = require('gulp-sass');
var path = {
sass_isstudy:'./modules/istudy/sass/',
};
gulp.task('default', function() {
// 将你的默认的任务代码放在这
});

gulp.task('sass', function() {
return gulp.src(path.sass_isstudy+'*.scss')
.pipe(sass.sync().on('error', sass.logError))
.pipe(gulp.dest('./modules/istudy/css'));
});

gulp.task('sass:watch', function() {
gulp.watch(path.sass_isstudy+'*.scss', ['sass']);
// gulp.watch('./modules/istudy/sass/*.scss').on('change',livereload);
}); //监控sass变化

3.3 命令执行

1
gulp sass:watch

执行上诉命令,在sass文件修改、保存后,gulp就会将sass文件转化为css文件

4.模块化编程

具体参见文章【javascript模块化编程】

4.1 ES5时代

以seajs为例:
CMD(Custom Module Definition)通用模块加载

4.1.1 seajs

引入seajs文件

1
<script type="text/javascript" src="../../common/jsext/sea-debug.js"></script>

seajs 的简单配置

1
2
3
4
5
6
7
8
seajs.config({
base: "../sea-modules/",
alias: {
"jquery": "jquery/jquery/1.10.1/jquery.js"
}
})
// 加载入口模块
seajs.use("../static/hello/src/main");//入口

定义模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 所有模块都通过 define 来定义
define(function(require, exports, module) {

// 通过 require 引入依赖
var $ = require('jquery');
var Spinning = require('./spinning');

// 通过 exports 对外提供接口
exports.doSomething = ...

// 或者通过 module.exports 提供整个接口
module.exports = ...

});

4.2 ES6时代

1
2
3
4
5
6
//bar.js
function hello(who){
return "hello "+who;
}

export {hello};
1
2
3
4
5
6
7
8
9
//foo.js
import {hello} from "bar";

var name = "zs";
function awe(){
console.log(bar.hello(name).toUpperCase());
}

export {awe};
1
2
3
4
5
6
7
//baz.js
import {bar} from "bar";
import {foo} from "foo";

console.log(bar.hello('张三'));//hello 张三

foo.awe();//HELLO ZS

当然现在需要使用babel转成es5,并且要使用打包工具browserify webpack rollup 等才能直接在现在的浏览器上运行。

5.组件化

组件化的思路是将一个模块独立开来,比如要写一个选择器按钮,将其分为三层:

数据层:用来决定按钮个数以及按钮是否选择
表现层:按钮使用现有的ui组件
逻辑层:按钮事件等逻辑处理

参考阅读:

  • Git 常用命令详解
  • SASS用法指南-阮一峰
  • SASS入门
  • [阮一峰-es6入门](http://es6.ruanyifeng.com/)

热力图实现原理

发表于 2018-10-10   |   分类于 项目总结   |  

1 使用方法

1)实例化一个热力图图层,传入图层ID号,并将其添加到地图上:

1
2
var heatmapLayer = new gEcnu.Layer.Heatmap('heatmapLayer');
map.addLayer(heatmapLayer);

2)为热力图添加数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
var point = {
x: worldPoint.x, //点的x坐标
y: worldPoint.y, //点的y坐标
value: val, //数值大小,最后表现为该点的颜色值
radius: radius //该点的半径大小
};

var data = {
max: max, //数据中的最大值
data: [point,point,point...] //其中point为每一个数据点
};

this.heatmapLayer.setData(data);

2 基本思路

利用 HTML5 提供的 Canvas API的createRadialGradient方法对每个点绘制出渐变圆形,再使用drawImage方法绘制到一个画布上;创建一个宽为256px,高为1px的矩形,利用createLinearGradient方法对其进行填色;

最后根据画布上每个点的透明度判读该点使用的颜色,透明度的值越大,颜色越红,值越小,颜色越蓝。

3 热力图的实现

3.1 插件形式的写法

3.1.1 支持模块化环境

通过写成自执行匿名函数的形式,并将变量名、作用域以及函数主体以参数的形式传入。判读当前处于什么模式,如果符合CMD规范,就使用module.exports的形式导出;如果符合AMD规范,就使用define()的形式导出;若都不满足,则在传入的作用域中添加一个以传入的变量名为键名的属性,值为传入的函数体的执行结果。

3.1.2 闭包的写法

将不必对外暴露的函数和变量放在闭包中,转变为私有变量。通过这种方式,外部无法改变私有变量的值,保证变量不会被篡改。

3.1.3 在文件开头加上分号

保证文件在被合并压缩时不会发生错误。因为文件的开头就是一对括号,在文件合并时,若上一个文件的最后是一个函数,就会产生直接执行该函数的结果,导致意想不到的错误,因此在文件的最开头初加上分号,防止错误的发生。

3.2 数据管理

3.2.1 数据存储

改变传入数据的形式,将每个点的值value和半径radius都保存在一个二维数组中,第一维为点的x左边,第二维为点的y坐标,这样便于比较传入的数据与已经存储的数据,若传入的数据点中有点的x坐标和y坐标都相同的情况,则将该点的值累加。

3.2.2 追加数据

可以向Store中追加数据,若数据点不止一个,而是以数组的形式传入,则递归处理数据。若传入数据的值小于原来数据的最大值,则在Canvas中追加绘制该点的图形;反之若大于最大值,则需要重新绘制整个Canvas的内容。

3.3 数据渲染

3.3.1 预渲染

由于数据量较大,将每个点绘制到画布时会导致频繁的重绘,因此考虑使用一个与目标Canvas画布同等大小且不可见的画布。由于离屏Canvas是不可见的,在DOM解析时,并不会对其进行渲染,只有将其再绘制到可见的目标画布上,才会解析样式进行渲染,因此绘制每一个点时都将其绘制到离屏画布中。

3.3.2 绘制每个数据点的黑白渐变圆

创建一个临时的Canvas 画布,画布的宽和高都设置为半径值的2倍。根据数据点的半径值,以及模糊数,绘制一个矩形,矩形的大小等于画布的大小。并利用Canvas API的createRadialGradient方法绘制渐变圆,设置渐变圆的起点颜色为黑色,终点颜色为白色,对矩形进行填充,填充的结果如图所示。对于不同的模糊数,生成的渐变圆效果有所不同,如图左边的渐变圆模糊数blur为0.15,左边的渐变圆模糊数blur为0.8,在系统中我们将其设置为0.15。

根据每个点的value在最小值与最大值之间的百分比,设置预渲染画布的透明度,并使用drawImage方法将每一个点的数据绘制到预渲染画布中,最后得到如下的效果:

1
2
shadowCtx.globalAlpha = (value-min)/(max-min);
shadowCtx.drawImage(tpl, rectX, rectY);

3.3.3 创建颜色模板

在一个临时的Canvas 画布中,将画布的宽设置为256px,高设置为1px,同时绘制一个同等大小的矩形,利用createLinearGradient方法创建一条线性颜色渐变对其进行填色,使用Canvas API的getImageData方法得到ImageData对象,该对象的data属性中存放着每个点的R(红色)G(绿色)B(蓝色)A(透明度)信息,因此我们得到的data的长度为256*4=1024,每四个元素对应一个点的信息。如图前四个元素对应第一个蓝色像素点的信息。

3.3.4 着色

对预渲染画布着色,并将其绘制到可见Canvas画布中。同样使用getImageData方法得到预渲染画布的ImageData对象,取得data属性中每隔4个元素的值,即每个点的透明度。依据该值,以及颜色模板,对该点着色,若该点的值为0则赋为第一个点的颜色蓝色,若该点的值为255,则赋为最后一个点的颜色红色。将该点的RGBA属性设置为对应颜色的RGBA属性。
修改完成后,使用Canvas API的putImageData方法,将预渲染画布中的内容绘制到可见画布上。

4 与ccgis平台的对接

4.1 热力图层

构造热力图层,继承自要素图层,初始化时将该图层的oClass属性设置为heatmapLayer

1
gEcnu.Layer.Heatmap = gEcnu.Layer.Feature.extend();

当将其添加到地图时,使用gHeatmap.create创建热力图。添加数据时,先对数据进行预处理,将传入的世界坐标转为屏幕坐标。缩放地图时,每一个数据点的半径也要相应缩放,因此对半径乘以缩放系数。
传入要素集时,首先取得视窗范围内的要素,并将每个要素的坐标转换为屏幕坐标,最后使用setData方法添加到图层中。

5 改进计划

目前使用随机数作为模拟数据,进一步可以考虑应用到真实数据中。

后台php实现数据实时抓取

发表于 2018-10-10   |   分类于 后台   |  

1、设置参数

date_default_timezone_set(“Asia/Shanghai”); //使得程序能够定时运行;
ignore_user_abort(); //即使Client断开(如关掉浏览器),PHP脚本也可以继续执行;
set_time_limit(0); //执行时间为无限制,php默认执行时间是30秒,可以让程序无限制的执行下去;
$interval=60*60; // 每隔一小时运行一次

2、定时数据抓取

在无限循环语句中,首先利用函数从url获取数据,然后将数据插入到数据库中(本文选用mysql数据库),最后使用sleep()函数设置等待一段时间后再进入下一次循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
do{  

/**********利用函数从url获取数据***************/
$json = GetCurl("http://www.pm25.in/api/querys/<all_cities class=" "></all_cities>json?token=jzxqS2qpsYU3bRhkEjey"); //json数据文件

/**********把上述数据插入数据库****************/
$con = mysql_connect("localhost","root","");//连接数据库
set_time_limit(0);//设置永远不超时
mysql_select_db("airquality_db",$con);//选择数据库
···

/**********按设置的时间等待一小时循环执行****************/
sleep($interval);

}while(true);

从url中获取数据的函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
function GetCurl($url){
set_time_limit(0);//设置永远不超时
$curl = curl_init();
curl_setopt($curl,CURLOPT_RETURNTRANSFER,1);//获取url数据作为变量存储,设置为1或true
curl_setopt($curl,CURLOPT_URL, $url);
//模拟用户使用的浏览器,在HTTP请求中包含一个"user-agent"头的字符串。
curl_setopt($curl,CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
$resp = curl_exec($curl);//运行curl,请求网页
$json = json_decode($resp,true);
curl_close($curl);//关闭curl请求
return $json;
}

3、数据抓取的网站

空气质量

http://www.pm25.in/api/querys/all_cities.json?token=jzxqS2qpsYU3bRhkEjey

气象

http://api.openweathermap.org/data/2.5/group?id=1796236,1799397,1808926,1799962,1790645&units=metric&APPID=94506fa9256f37bd13eae4126beb7d8d&lang=zh_cn
Id代表城市,APPID代表密钥,lang代表语言。
更新时间好像是我自己测的,我让它每隔半小时获取一次,然后看到数据大约是一小时一次。

4、存在问题

数据抓取过程中发生断开连接现象(即循环过程中断),需要重新运行PHP文件进行抓取。

在线地图配准实现原理

发表于 2018-10-10   |   分类于 项目总结   |  

1 基本思路

1) 利用 HTML5 提供的 File API读取<input>标签中的内容,使用FileReader对象的readAsDataURL方法对文件进行base-64编码,然后将图片的src属性设置为编码后的Data URL。

2) 创建一个与地图同宽高的<canvas>标签,将图片的内容通过Canvas API中的drawImage方法绘制到canvas中,之后对图片的缩放、平移、旋转等操作都是在canvas上进行操作。

3) 当处于配准状态,即canvas的z-index属性设置为最高时,绑定mousedown事件,记录下相对于原始图片的坐标,以及屏幕坐标,并将屏幕坐标改为地理坐标。在地图及<canvas>元素上分别标注出该点的位置及标注的序号。

4) 判断控制点个数,大于等于4个点则调用geoserver服务器的仿射变换服务,返回变换参数和误差估计,若计算出的误差较大可删除误差较大的控制点调整标准差。

5) 上传文件及配准信息:将图片保存问tif格式保存到后台服务器;将控制点信息保存为cpt格式的文件,cpts为json格式数组[{cpt},{cpt},{cpt},…],其中cpt:{ “ptNo”:”pt1”;
x0:double;y0: double;x1:double;y1:double;}(其中:x0,y0:屏幕坐标,x1,y1:地理坐标);将仿射变换服务返回的信息保存为twf格式的文件,tfw辅助文件是一个包含六行内容的ASCII文本文件,每行为一个数值,含义如下:

  • X方向上的象素分辨素
  • X方向的旋转系数
  • Y方向的旋转系数
  • Y方向上的象素分辨率
  • 栅格地图左上角象素中心X坐标
  • 栅格地图左上角象素中心Y坐标

6) 最后在数据库表中添加图层信息,具体操作为:向g_ftset表中添加一条记录,保存tfw
文件的路径;向g_layers添加一条记录,其中ftset_id为刚刚插入的主键id,yrtype:geoimg/tfw; path:tfw格式文件。

2 关键技术

2.1 实现与图片交互

创建一个对象存储图片的信息,包括图片的位置(top、left);图片大小(width、height)
缩放比例(scalex、scaley);倾斜角度(theta);透明度(opacity);标注点信息(markers)等。

利用以上信息计算图片的四个角的屏幕坐标,计算过程如下:
1)根据图片宽高及缩放比例计算图片目前的宽高值。
2)计算左上角屏幕坐标

1
2
3
4
5
6
7
8
9
10
11
12
//计算对角线长度、对角线与底边的夹角
this._hypotenuse = Math.sqrt(Math.pow(this.currentWidth / 2, 2) +
Math.pow(this.currentHeight / 2, 2));
this._angle = Math.atan(this.currentHeight / this.currentWidth;
//计算x、y方向的偏移量
var offsetX = Math.cos(this._angle + this.theta) * this._hypotenuse;
var offsetY = Math.sin(this._angle + this.theta) * this._hypotenuse;
//计算左上角坐标
var tl = {
x: this.left - offsetX,
y: this.top - offsetY
};

3)计算另外三个点屏幕坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//根据倾斜角计算其sin值、cos值
var sinTh = Math.sin(theta);
var cosTh = Math.cos(theta);
//计算右上角坐标
var tr = {
x: tl.x + (this.currentWidth * cosTh),
y: tl.y + (this.currentWidth * sinTh)
};
//计算右下角坐标
var br = {
x: tr.x - (this.currentHeight * sinTh),
y: tr.y + (this.currentHeight * cosTh)
};
//计算右上角坐标
var bl = {
x: tl.x - (this.currentHeight * sinTh),
y: tl.y + (this.currentHeight * cosTh)
};

4)设置每个点的四个坐标
根据角落大小(cornersize)、倾斜角(theta)及之前计算出的四个角的屏幕坐标计算每个角的左上角、右上角、右下角及左下角屏幕坐标。

2.1.1 图片平移

1)绑定<canvas>元素的鼠标按下mousedown事件,判断单击的点是否被图片包含,判断方法如下:
根据四个角的坐标点,组成上、右、下、左四条边:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

{
topline: {
o: oCoords.tl,
d: oCoords.tr
},
rightline: {
o: oCoords.tr,
d: oCoords.br
},
bottomline: {
o: oCoords.br,
d: oCoords.bl
},
leftline: {
o: oCoords.bl,
d: oCoords.tl
}
}

当鼠标的y轴值位于四个点的y轴最大值与最小值之间时,计算坐标点所在的水平线上
与四条边相交且x轴值大于鼠标点击的x值的点的个数(其实是向右做水平方向的射线),若相交的个数为奇数,则点击到了图片。

2)判断点击的坐标点是否被包含在四个角落的范围内,如果是则说明当前处于缩放状态,否的话则说明目前处于平移状态,判断方法同上。记录下offsetX值和offsetY值:

1
2
var offsetX = mp.ex - oImg.left;
var offsetY = mp.ey - oImg.top;

3)绑定<canvas>元素的鼠标按下mousemove事件,当鼠标移动时触发事件,执行平移图片操作时动态修改图片的top、left值:

1
2
oImg.left = mp.ex - offsetX;
oImg.top = mp.ey - offsetY;

4)最后清空canvas画布中的内容,利用Canvas API的drawImage方法按照图片的原始大小将图片重新绘制到canvas元素中,并利用Canvas API的context.translate(oImg.left, oImg.top)平移canvas中的内容。

2.1.2 缩放、旋转图片

1)若鼠标点被包含在四个角落的范围内,说明当前处于缩放、旋转状态,记录下当前
的鼠标位置、缩放值(scalex)和倾斜角度(theta):

1
2
3
4
5
6
7
8
this._currentTransform = { 
ex: mp.ex,
ey: mp.ey,
left: oImg.left,
top: oImg.top,
scalex: oImg.scalex,
theta: oImg.theta
};

2)移动鼠标时,动态计算当前图片的缩放大小scalex:

1
2
3
4
5
6
7
//计算鼠标点击时的坐标与左上角距离以及当前鼠标位置与左上角距离
var lastLen =Math.sqrt(Math.pow(this._currentTransform.ey - this._currentTransform.top, 2)
+Math.pow(this._currentTransform.ex - this._currentTransform.left, 2));
var curLen = Math.sqrt(Math.pow(mp.ey - this._currentTransform.top, 2)
+Math.pow(mp.ex - this._currentTransform.left, 2));
//根据之前的缩放值计算当前缩放值
var scale = this._currentTransform.scalex * (curLen / lastLen);

3)移动鼠标时,动态计算当前图片的倾斜角度theta:

1
2
3
4
5
6
7
8
9
10
11
12
//鼠标点击时,图片左上角垂线与左上角和鼠标点的连线构成的角度
var lastAngle = Math.atan2(
this._currentTransform.ey - this._currentTransform.top,
this._currentTransform.ex - this._currentTransform.left
);
//鼠标移动时,图片左上角垂线与左上角和鼠标点的连线构成的角度
var curAngle = Math.atan2(
mp.ey - this._currentTransform.top,
mp.ex - this._currentTransform.left
);
//动态计算当前图片的倾斜角度
var theta = (curAngle - lastAngle) + this._currentTransform.theta;

4)清空canvas画布中的内容,利用Canvas API的drawImage方法按照图片的原始大小将图片重新绘制到canvas元素中,并利用Canvas API的context.rotate(oImg.theta)和context.scale(oImg.scalex, oImg.scaley)旋转和缩放canvas中的内容。

2.2 计算控制点图片坐标

捕获鼠标点击事件,得到鼠标点坐标,计算坐标点与图片左上角点的x、y轴的差值:

1
2
var dltX = mp.ex - tl.x;
var dltY = mp.ey - tl.y;

计算坐标点与左上角点连线的长度以及左上角垂线与连线的角度:

1
2
var angle = Math.atan(dltX / dltY);
var dis = Math.sqrt(Math.pow( dltX , 2) + Math.pow( dltY , 2));

最后计算坐标点相对于图片左上角的坐标值:

1
2
var coordX = parseInt( Math.abs( Math.sin(angle+theta) * dis ) / oImg.scalex );
var coordY = parseInt( Math.abs( Math.cos(angle+theta) * dis ) / oImg.scalex );

3 改进计划

3.1 现实配准功能

目前只实现了将配准信息以及仿射变换的结果保存并上传到服务器中,但是还不能将tif文件作为一个图层加载到地图中,接下来进一步实现将其作为图层加载。

3.2 再次修改控制点

可利用已经保存的控制点信息,提供再次修改的功能,这样用户可以在上一次配准工作的基础上继续完成配准操作,保证配准结果的准确性。

前端模块化

发表于 2018-10-10   |   分类于 前端技术   |  

1 ES5时代

1.1 原生代码实现模块化

1) 对象写法

1
2
3
4
5
6
7
8
9
var module1 = new Object({
    _count : 0,
    m1 : function (){
      //...
    },
    m2 : function (){
      //...
    }
  });

2) 立刻执行函数(Immediately-Invoked Function Expression,IIFE) or 匿名闭包

1
2
3
4
5
6
7
8
9
10
11
12
13
var module1 = (function(){
    var _count = 0;
    var m1 = function(){
      //...
    };
    var m2 = function(){
      //...
    };
    return {
      m1 : m1,
      m2 : m2
    };
  })();

这样可以很好的保护私有变量,通过return来设置公开的方法。缺点也有: 动态添加方法的时候比较麻烦,且无法修改内部私有变量。
3) 放大模式 or 宽放大模式(Loose augmentation)
如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用”放大模式”(augmentation)

1
2
3
4
5
6
var module1 = (function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(module1);

在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用”宽放大模式”

1
2
3
4
var module1 = ( function (mod){
    //...
    return mod;
  })(window.module1 || {});

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var moduleTest = ( function (mod){

var value1=0, privateName='张三';

function privateAddTopic(data) {
// 这里是内部处理代码
console.log("内部函数");
console.log(data);
}
mod.name = privateName;
mod.init=function(){
this.f1();
};
mod.f1=function(){
console.log("f1--hello");
}; 
mod.f2=function(data){
privateAddTopic(data);
};   
  return mod;
})(window.moduleTest || {});

moduleTest.init();
moduleTest.f2("hello world");
console.log(moduleTest.name); //张三
moduleTest.name = '李四'; //修改模块中的属性
console.log(moduleTest.name); //李四

1.2 CommonJS/AMD/CMD

现代模块的基本思想实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

var myModules = (function Manager() {
var modules = {}; //定义的模块保存在这个对象里面

function define(name, deps, impl) {//impl是implement的简写,实现的方法
for (var i = 0; i < deps.length; i++) {
deps[i] = modules[deps[i]]; //从保存的对象中获取依赖的模块,注:依赖的模块,肯定已经被define()存放在modules对象中了。
}

modules[name] = impl.apply(impl, deps);//如果此时的模块引入别的模块deps,就将deps作为impl实现的方法的参数
}
/**
* [get 通过名字获得模块]
* @param {[type]} name [模块名]
* @return {[type]} [完整独立模块]
*/
function get(name) {
return modules[name];
}

return {
define: define,
get: get
};
})();

//测试
myModules.define("bar", [], function() {
function hello(who) {
return "hello " + who;
}
return {
hello: hello
};
});
myModules.define("foo", ["bar"], function(bar) {
var n = '张三';
function awe(who) {
console.log(bar.hello(n).toUpperCase());
}
return {
awe: awe
};

});

var bar = myModules.get("bar");
var foo = myModules.get("foo");

foo.awe();//HELLO 张三

1) CommonJS
node.js的模块系统,就是参照CommonJS规范实现的

1
2
var math = require('math');
math.add(2,3); // 5

第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。这就催生AMD规范到来的背景。

2) AMD
AMD(Asynchronous Module Definition)是异步模块加载的意思,会预执行(依赖)模块。

1
require([module], callback);

1
2
3
require(['math'], function (math) {
  math.add(2, 3);
});

主要有两个Javascript库实现了AMD规范:require.js和curl.js

3) CMD
CMD(Custom Module Definition)通用模块加载,SeaJS遵循CMD规范,并行加载所有依赖的模块, 但不会立即执行模块,等到真正需要(require)的时候才开始解析(懒执行)。
引入SeaJS文件

1
<script type="text/javascript" src="../../common/jsext/sea-debug.js"></script>

SeaJS 的简单配置

1
2
3
4
5
6
7
8
seajs.config({
base: "../sea-modules/",
alias: {
"jquery": "jquery/jquery/1.10.1/jquery.js"
}
})
// 加载入口模块
seajs.use("../static/hello/src/main");//入口

定义模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 所有模块都通过 define 来定义
define(function(require, exports, module) {

// 通过 require 引入依赖
var $ = require('jquery');
var Spinning = require('./spinning');

// 通过 exports 对外提供接口
exports.doSomething = ...

// 或者通过 module.exports 提供整个接口
module.exports = ...

});

另外可以使用seajs-text加载html文件或者tpl片段,seajs-css加载css文件

1
2
3
4
5
6
7
8
9
10
11
12
13
<script src="path/to/sea.js"></script>
<script src="path/to/seajs-text.js"></script>

<script>
define("main", function(require) {

// You can require `.tpl` file directly
var tpl = require("./data.tpl")
//或者html
var html =require("./a.html");
$('.some_class').append(html);
})
</script>

seajs-css

1
2
3
4
5
6
7
8
9
10
<script src="path/to/sea.js"></script>
<script src="path/to/seajs-css.js"></script>

<script>

// seajs can load css file after loading css plugin.
seajs.use("path/to/some.css");
//很多时候可以使用require的方式
require("path/to/some.css");
</script>

2 ES6时代

1
2
3
4
5
6
//bar.js
function hello(who){
return "hello "+who;
}

export {hello};
1
2
3
4
5
6
7
8
9
//foo.js
import {hello} from "bar";

var name = "zs";
function awe(){
console.log(bar.hello(name).toUpperCase());
}

export {awe};
1
2
3
4
5
6
7
//baz.js
import {bar} from "bar";
import {foo} from "foo";

console.log(bar.hello('张三'));//hello 张三

foo.awe();//HELLO ZS

当然现在需要使用babel转成es5,并且要使用打包工具browserify webpack rollup 等才能直接在现在的浏览器上运行。

参考阅读:

  • Javascript模块化编程(一):模块的写法
  • Javascript模块化编程(二):AMD规范
  • Javascript模块化编程(三):require.js的用法
  • seajs-github
  • seajs官网
  • seajs-text
  • 《你不知到的javascipt》
  • 阮一峰-es6入门

上传下载csv数据

发表于 2018-10-10   |   分类于 前端技术   |  

一、上传csv数据

1
2
3
4
5
6
7
8
9
10
var file = document.getElementById(fileid).files[0];
if(file){
var reader = new FileReader();
reader.readAsText(file,'GB2312');
reader.onload = function(f){
//console.log(f);
var datatmp = this.result;
var tmparr = datatmp.split(/\r\n/g);
}
}

二、下载csv数据

方法一:datatable自带功能

引入文件

1
2
3
<link href="common/csslib/dataTables.tableTools.min.css">
<script src='common/jslib/publib/dataTables.tableTools.min.js'
charset="utf-8"></script>

配置参数

1
2
3
4
5
6
7
8
9
10
11
"oTableTools": {
"sSwfPath": "/slprj/common/csslib/copy_csv_xls.swf",
"aButtons": [
{
"sExtends": "csv",
"sButtonText": "导出数据",
"sCharSet": "utf8",
"bBomInc": true,
}
]
},

方法二:利用<a>标签下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a=document.createElement('a');
a.id='downloadFtsetBtn';
a.style.display='none';
a.target='_blank';
document.body.appendChild(a);
a.onclick = function (){
_a = document.getElementById('downloadFtsetBtn');
document.body.removeChild(_a);
}

var blob = new Blob([exportsda], {type: 'text'});
var URL=window.URL || window.webkitURL;
a.href=URL.createObjectURL(blob);
a.download = 'aa.csv';
a.click();

方法三:利用<iframe>标签下载

1
2
3
4
var elemIF = document.createElement("iframe");   
elemIF.src = req;
elemIF.style.display = "none";
document.body.appendChild(elemIF);

方法四:window.open

1
window.open(url);//但是该方法在火狐上没有效果的,在IE浏览器上是可以的。

方法五:window.location

1
window.location.href="htpp://www.baidu.com/test.rar";//火狐有些版本是不支持的。

为了解决这个问题,我们可以换做另外一种方法

1
window.location="htpp://www.baidu.com/test.rar";

this指针总结

发表于 2018-10-10   |   分类于 前端技术   |  

文章只是简单列举了方式和一些会改变this指针的情况

1.探寻之,必昭然若揭

  1. new绑定 this–>新创建的对象
    var bar = new foo()
  2. call/bind 硬绑定 this–>指定的对象
    var bar = foo.call(obj2)
  3. 隐式绑定 this–>上下文对象
    var bar = obj1.foo()
  4. 默认绑定 this–>全局对象window

四种情况也是按照优先级排列的

2.实践之,定了然于胸

2.1 回掉函数会改变this指针

绑定

1
2
3
dbTools.queryUsrDB2Datas(function(){
usrResDiv.fyDiv.apply(usrResDiv,arguments);
});

2.2 setTimeout/setinterval函数会改变this指针(例子见第三部分)

2.3 绑定的例外

  • foo.call(null) 使用null或者undefined,忽略传入对象的this,实际运用的是默认绑定,这也是这样方法的弊端,this指向window。
    修改var DMZ = Object.create(null); foo.apply(DMZ,[2,3]);

  • 间接引用

1
2
3
4
5
6
7
8
9
10
11

function foo(){
console.log(this.a);
}
var a = 2;
var o = {a:3,foo:foo};
var p = {a:4};

o.foo();//3
(p.foo = o.foo)(); //2 this-->window
p.foo(); //4

p.foo = o.foo返回值是目标函数的引用,因此调用位置是foo(),而不是p.foo(),o.foo();

3.避免之,需谨小事微

除了第一部分的方法外,还有一些常用的方法。

3.1 ES5中我们经常会使用self = this,如:

1
2
3
4
5
6
7
8
9
10
11
12

function foo(){
var self = this;
setTimeout(function(){
console.log(self.a);
},100);
}

var obj = {
a:2;
}
foo.call(obj);//2

3.2 ES6中的箭头函数(this词法)

1
2
3
4
5
6
7
8
9
10
function foo(){
setTimeout => {
console.log(this.a);//this继承来自foo()
},100);
}

var obj = {
a:2;
}
foo.call(obj);//2

sublime-快捷键

发表于 2018-10-10   |   分类于 前端技术   |  

选择类

Ctrl+D 选中光标所占的文本,继续操作则会选中下一个相同的文本。

Alt+F3 选中文本按下快捷键,即可一次性选择全部的相同文本进行同时编辑。举个栗子:快速选中并更改所有相同的变量名、函数名等。

Ctrl+L 选中整行,继续操作则继续选择下一行,效果和 Shift+↓ 效果一样。

Ctrl+Shift+L 先选中多行,再按下快捷键,会在每行行尾插入光标,即可同时编辑这些行。

Ctrl+Shift+M 选择括号内的内容(继续选择父括号)。举个栗子:快速选中删除函数中的代码,重写函数体代码或重写括号内里的内容。

Ctrl+M 光标移动至括号内结束或开始的位置。

Ctrl+Enter 在下一行插入新行。举个栗子:即使光标不在行尾,也能快速向下插入一行。

Ctrl+Shift+Enter 在上一行插入新行。举个栗子:即使光标不在行首,也能快速向上插入一行。

Ctrl+Shift+[ 选中代码,按下快捷键,折叠代码。

Ctrl+Shift+] 选中代码,按下快捷键,展开代码。

Ctrl+K+0 展开所有折叠代码。

Ctrl+← 向左单位性地移动光标,快速移动光标。

Ctrl+→ 向右单位性地移动光标,快速移动光标。

shift+↑ 向上选中多行。

shift+↓ 向下选中多行。

Shift+← 向左选中文本。

Shift+→ 向右选中文本。

Ctrl+Shift+← 向左单位性地选中文本。

Ctrl+Shift+→ 向右单位性地选中文本。

Ctrl+Shift+↑ 将光标所在行和上一行代码互换(将光标所在行插入到上一行之前)。

Ctrl+Shift+↓ 将光标所在行和下一行代码互换(将光标所在行插入到下一行之后)。

Ctrl+Alt+↑ 向上添加多行光标,可同时编辑多行。

Ctrl+Alt+↓ 向下添加多行光标,可同时编辑多行。

编辑类

Ctrl+J 合并选中的多行代码为一行。举个栗子:将多行格式的CSS属性合并为一行。

Ctrl+Shift+D 复制光标所在整行,插入到下一行。

Tab 向右缩进。

Shift+Tab 向左缩进。

Ctrl+K+K 从光标处开始删除代码至行尾。

Ctrl+Shift+K 删除整行。

Ctrl+/ 注释单行。

Ctrl+Shift+/ 注释多行。

Ctrl+K+U 转换大写。

Ctrl+K+L 转换小写。

Ctrl+Z 撤销。

Ctrl+Y 恢复撤销。

Ctrl+U 软撤销,感觉和 Gtrl+Z 一样。

Ctrl+F2 设置书签

Ctrl+T 左右字母互换。

F6 单词检测拼写

搜索类

Ctrl+F 打开底部搜索框,查找关键字。

Ctrl+shift+F 在文件夹内查找,与普通编辑器不同的地方是sublime允许添加多个文件夹进行查找,略高端,未研究。

Ctrl+P 打开搜索框。举个栗子:1、输入当前项目中的文件名,快速搜索文件,2、输入@和关键字,查找文件中函数名,3、输入:和数字,跳转到文件中该行代码,4、输入#和关键字,查找变量名。

Ctrl+G 打开搜索框,自动带:,输入数字跳转到该行代码。举个栗子:在页面代码比较长的文件中快速定位。

Ctrl+R 打开搜索框,自动带@,输入关键字,查找文件中的函数名。举个栗子:在函数较多的页面快速查找某个函数。

Ctrl+: 打开搜索框,自动带#,输入关键字,查找文件中的变量名、属性名等。

Ctrl+Shift+P 打开命令框。场景栗子:打开命名框,输入关键字,调用sublime text或插件的功能,例如使用package安装插件。

Esc 退出光标多行选择,退出搜索框,命令框等。

显示类

Ctrl+Tab 按文件浏览过的顺序,切换当前窗口的标签页。

Ctrl+PageDown 向左切换当前窗口的标签页。

Ctrl+PageUp 向右切换当前窗口的标签页。

Alt+Shift+1 窗口分屏,恢复默认1屏(非小键盘的数字)

Alt+Shift+2 左右分屏-2列

Alt+Shift+3 左右分屏-3列

Alt+Shift+4 左右分屏-4列

Alt+Shift+5 等分4屏

Alt+Shift+8 垂直分屏-2屏

Alt+Shift+9 垂直分屏-3屏

Ctrl+K+B 开启/关闭侧边栏。

F11 全屏模式

Shift+F11 免打扰模式

123
Leo_婷子酱

Leo_婷子酱

30 日志
6 分类
24 标签
GitHub
© 2018 Leo_婷子酱
由 Hexo 强力驱动
主题 - NexT.Pisces