本文首发于掘金,转载请注明出处,保留原文连接以及做者信息。javascript
这篇文章不算是技术干货,你大能够带着看热闹的轻松心态来瞧瞧,我,一个新郎,是怎么让结婚这事儿变得更加艰难的。 php
就在上个月,我举办了婚礼,如愿以偿地和心爱的姑娘缔结了一辈子的誓约。css
在筹备婚礼的时候,咱们就下定决心,要尽可能不落俗套,尽可能让参加婚礼的亲友感觉到咱们用心策划了每个细节。做为重要一环的电子请柬,固然也不能将就。html
现在,电子婚礼请柬已经是一种常见之物。这种请柬通常都是婚庆 App 生成的 H5 页面,展现婚纱照和婚礼的时间、地点等,这两年还增长了送礼物、发祝福弹幕之类的功能。相信你们常常会在朋友圈见到朋友分享的请柬。前端
我是一个喜欢折腾的人,我喜欢用我所学的知识解决生活中的问题。那些通用的模板显然不能知足我。好巧不巧,我正好是一名前端开发者,出于敬业态度和极客精神,我决定本身作这份请柬。java
想一想也没啥难度嘛:弄一台云服务器,简单搭个网站,本身写页面,丢到服务器上,齐活。你看,一点都不难!ios
才怪。git
开头吹的气壮山河,但这第一脚就踢到了铁板上 —— 我不会作 UI 设计。我审视着镜子里的本身:Photoshop 水平二半吊子、我的网站的配色曾被面试官说辣眼睛、惟一一次被夸画画好是幼儿园时候…… 资质鲁钝,如今才开始提高审美、学习平面设计确定是来不及了。我着急啊,我上火啊,我一缕一缕地揪头发啊……程序员
等等,我是什么来着?程序员啊,个人看家本领是什么来着?面向搜索引擎编程啊!既然不能本身产出,只好践行拿来主义了。因而我 Google 到了许多电子请柬的模板(关键词:RSVP)。Why Google?防止和国内经常使用的模板撞脸。通过和新娘的反复讨论,咱们敲定了一套风格最吸引咱们的模板。github
总算是有米可炊了。
云服务器我选的是阿里云的 ECS。由于只是用来放静态页面,因此不须要豪华配置,简单清爽(费用低)就好:
既然咱是前端工程师,那就正好借助近水楼台,用 Node.js 来搭建 Web 服务。参考阿里云的官方文档,我在 CentOS 里安装好了 Node 环境。框架方面我选用了 Express,由于它适合我(这种新手)。其实像电子请柬这种简单的应用,框架并不是是必需品,但这是一个体验新鲜事物的机会不是吗?因而用 Express 搭建了简单的服务:
let http = require('http');
let express = require('express');
let app = express();
app.use(express.static('html'));
http.createServer(app).listen(80);
复制代码
如今我能够访问到个人服务器中的网页,只不过其连接是这种原始人形态:http://127.127.127.127:80
。对于一封婚礼请柬来讲,这也太影响气质了吧。我把这样的连接发给亲友,人家还不觉得我是盗号的?
不行不行。
玉皇大帝说:要有域名。因而便注册了域名。解析到服务器上后,就能够经过域名访问到本身的页面了。
另外,网站是要进行 ICP 备案的。你会须要填写一些表格,域名服务商(个人是阿里云)会寄给你一张大大的蓝色备案幕布,让你在幕布前拍照上传。这是一个不太复杂但必不可少、不算有趣但让我莫名感受神圣的程序。最终你会获得一串备案号(如京ICP证030173号)。
在去年的一次小尝试中,我想把微信群二维码贴在墙上供邻居扫码加群。但群二维码是有时效的,按期打印新的二维码有点浪费纸。当时我刚好有本身的网站,就想了个办法:纸上打印一个网址的二维码,该网址指向个人网站中的一张图片 —— 微信群二维码,这样,我只须要按期上传新的群二维码便可。但测试时发现微信会拦截 http 协议的外链,须要用户确认访问方可进入页面(如今已经再也不拦截)。彼时我没有余力继续研究 https,只好做罢。(如今想一想,用第三方图床的连接应该就能够。)
因此 https 算是我不曾征服的疆土,此次我必定要把它拿下。
为了不增长没必要要的工(fèi)做(yòng),我选择了免费的 SSL 证书:
一手交钱一手交货,而后走个流程:
当流程走到最后一步 —— 下载证书时,却没有看到 Node.js 服务器专用的证书:
幸亏找到了一位前辈的文章,告诉我下载 Nginx 的就能够。有前辈撑腰,腰不酸腿不疼,一口气配完 https 不费劲:
let http = require('http');
let express = require('express');
let fs = require('fs');
let https = require('https');
//读取证书文件
let options = {
key: fs.readFileSync('./0000000_域名.key'),
cert: fs.readFileSync('./0000000_域名.pem'),
};
let app = express();
app.use(express.static('html'));
http.createServer(app).listen(80);
https.createServer(options,app).listen(443);
复制代码
如今就能够用 https 协议的域名来访问请柬啦!
PM2 能够帮你在更新代码后自动重启服务,老省事儿了。
忙活完这些,后端支撑基本就绪,能够开始着手开(魔)发(改)页面了。
先请你们检阅一下咱们夫妇的审美水平:这是咱们选中的模板。风格清新明快,传达出舒适甜蜜的情感信息。
页面中有些内容模块是不适用于咱们的:
OK,砍掉这些无用部分,再将模板中的图片和文案替换为咱们本身的信息,一封属于咱们的电子请柬就初具雏形了。
但仅仅这样仍是不行的,还须要稍稍整理一下代码。
首先面临的就是字体问题。
原模板使用的字体是 Oswald:
此款字体瘦高无衬线,效果优雅悦目,但不支持中文。恰当的字体可以提高用户体验,若是中文字体的风格与 Oswald 不符,总体感观将大打折扣。我须要一款风格相近的中文字体。
我在字由中反复检索比对,终于挖到了宝藏 —— 方正俊黑,该系列的方正俊黑_纤和方正俊黑_粗很有原模板字体的神韵:
这两款字体是非商用免费受权的,因此我能够放心大胆地使用。
在方正官网上,想下载字体你得是设计师才行(见上图字体名称后面的 Tag)。但有趣的是,只要你注册帐号时将身份勾选为设计师便可,官方仍是挺厚道的。(从今天起我就是方正字库认证的设计师了)
![]()
在 CSS 中将字体设置为方正俊黑,页面效果恰如我愿。
可是!你们都知道,中文字体文件的体积,那简直,五大三粗。
请柬内容并无那么多字,若是线上使用完整的字体包,必然拖慢加载速度。我把这样的请柬发给亲友,人家还不觉得我是混子前端?
在百度上略微搜索就会发现,遇到这种问题的开发者不在少数,但网上的大多求助帖没有获得让我满意的解决方案。几经展转,我发现了字蛛+,这个工具能帮咱们自动分析并按需压缩字体文件。只需将本地代码路径或线上连接写进配置文件,而后运行 fsp run
便可完成。下图是我将字体进行分析压缩后的效果:
这样的压缩效果,真让人神清气爽啊~~~(至于 FZJunHJW_Cu.TTF
为什么没变,容我先卖个关子)
视差滚动,这个效果你们确定不陌生吧?当你在模板页面中上下滑动页面时会发现,有些 section 的背景图片与内容并非相对静止的,这种效果大大增长了页面的动(bī)感(gé),是我不管如何都想应用在请柬中的效果。
咱们发送请柬的途径只有一个,那就是微信聊天。不管是热衷于在手机上抢红包的长辈,仍是习惯在工做时将微信挂在电脑上的同龄朋友,微信绝对是最有效率的传达方式。
故此须要在桌面端(PC、Mac)和手机端的微信中测试个人请柬。
桌面端效果正常;但在手机端,视差滚动效果失灵了。
咱们知道,实现视差滚动效果最简单的方式就是用 background-attachment: fixed;
,而模板页面的作法,是在此基础上增长了动态修改 background-position
,以实现滚动时背景图片以较慢的速度跟随(而不是 fix 在原地)。现在在移动端失效,想必是兼容性问题。让咱们到 caniuse.com 中查证一下:
果不其然,微信内置的 QQ 浏览器 X5 内核不支持这一 CSS 特性。有种说法是这一效果会给移动端浏览器带来性能上的压力,影响体验,因此干脆不支持该特性。
若是在手机微信中打开的请柬没有动感的效果,那将显得多么平庸啊。
在搜遍了古今中外全部技术贴而无果后,我决定用 JavaScript 来实现,正所谓以暴制暴、一力降十会……
思路是这的:
<img />
标签的方式加载到页面中,藏在视口上方;是否是感受有点儿懵?瞧我这嘴,笨得跟棉裤腰似的,给你看代码你就懂了:
<!-- 背景图片 -->
<img src='img.png' />
<!-- 应用特效的 section -->
<section id='section'>All eyes on me.</section>
复制代码
img {
position: fixed;
top: 0;
left: 0;
transform: translateY(-100%);
}
复制代码
$(document).scroll(function () {
var windowScrlTop = $(window).scrollTop();
var windowH = $(window).height();
if (
($('#section').offset().top - windowScrlTop) <= windowH
&& ($('#section').offset().top - windowScrlTop) >-$('#section').height()
){
// 当 section 位于视口中时:
$('img').css('transform', 'translateY(0)');
} else {
// 当 section 位于视口之外时:
$('img').css('transform', 'translateY(-100%)');
}
});
复制代码
经过这样的控制,就能够在微信内置浏览器中实现视差滚动的效果。无从验证这样作是否比 background-attachment: fixed;
性能要好,但从真机测试来看,上下滑动流畅,未出现卡顿。
我把页面优美、效果炫酷的请柬发给了新娘,已经准备好了接受汹涌而至的赞美和香吻 —— “我们的请柬里怎么没有地图呀?我看人家的都有呢?”
😳哦,新需求是吧,好的我知道了。
在请柬中加入地图,目的就是明确婚礼地点的位置信息,方便亲友抵达。这就须要:
因为主要在微信中使用,我就无脑选择了腾讯地图。 在腾讯位置服务上注册帐号、验证身份、申请开发密钥以后,便可开始开发。这里我用的是 JavaScript 地图 API。
先在请柬页面中加载 API 服务:
<script charset="utf-8" src="https://map.qq.com/api/js?v=2.exp&key=YOUR_KEY"></script>
//“YOUR_KEY” 即为你本身的开发密钥
复制代码
再设置容器元素并指定指定宽高:
<div id="map" style="width:500px; height:300px"></div>
复制代码
接下来须要初始化:
var centerLatlng = new qq.maps.LatLng(39.908551, 116.397743);//地图的中心点的经纬度。
var myOptions = {
zoom: 15,
center: centerLatlng,
mapTypeId: qq.maps.MapTypeId.ROADMAP
}
var map = new qq.maps.Map(document.getElementById("map"), myOptions);//地图实例
复制代码
此时,展现地图的目的就达到了。可是为了提高交互体验,还须要作一些工做:
将婚礼地点标记出来。
//在地图上添加标注:
var anchor = new qq.maps.Point(10, 10),//相对于标记点的位置
size = new qq.maps.Size(24, 24),//显示尺寸
origin = new qq.maps.Point(0, 0),//相对于图片左上角的相对像素坐标
icon = new qq.maps.MarkerImage('./img/center.gif', size, origin, anchor);//用于标记的图片的路径
var marker = new qq.maps.Marker({
icon,
position: centerLatlng,
map,
});
复制代码
用文字提示用户标记点是支持点击的:
var label = new qq.maps.Label({
position: centerLatlng,
map,
content: '点击红点查看导航',
style: {
margin: '-18px -10px 0px 10px'
}
});
复制代码
实现点击后打开 App 导航(移动端)或详情地图页面(桌面端)的功能。若是是桌面设备,在新网页中打开以婚礼地点为中心的地图便可;可要是移动端,就要稍微繁琐一些:借助微信内置的地图服务调用第三方地图 App(百度地图、高德地图等)展现导航路线 ——— 想拔出这根萝卜,可得带出很多泥来。
恭喜我本身,解锁了支线任务。
那么,接下来的步骤就是:
注册微信公众号。
在公众号的【设置 - 公众号设置 - 功能设置】中配置 JS 接口安全域名。
获取 access_token:
//服务端 Node.js 代码:
const request = require('request');
request.get({
uri: 'https://api.weixin.qq.com/cgi-bin/token',
json: true,
qs: {
grant_type: 'client_credential',
appid: YOUR_APPID,
secret: YOUR_APPSECRET
}
}, (err, res, body) => {
if (err) {
console.log(err)
return
}
if (body.errcode) {
return
}
console.log('返回值:',res);// {"access_token":"艾克赛斯_套肯","expires_in":7200}
}
);
复制代码
获取 jsapi_ticket
:
//服务端 Node.js 代码:
const request = require('request');
request.get({
uri: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket',
json: true,
qs: {
access_token: access_token,
type: 'jsapi'
}
}, (err, res, body) => {
if (err) {
console.log(err)
return
}
if (body.errcode) {
return
}
console.log('返回值:',res);
/* 返回值示例: { "errcode":0, "errmsg":"ok", "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", "expires_in":7200 } */
}
);
复制代码
根据签名算法生成签名:
// 服务端 Node.js 代码:
const crypto = require('crypto');
// sha1加密
function sha1(str) {
let shasum = crypto.createHash("sha1")
shasum.update(str)
str = shasum.digest("hex")
return str
}
// 生成时间戳
function createTimestamp() {
return parseInt(new Date().getTime() / 1000) + ''
}
// 生成随机串
function createNonceStr() {
return Math.random().toString(36).substr(2, 15)
}
// 字典排序
function raw(args) {
var keys = Object.keys(args)
keys = keys.sort()
var newArgs = {}
keys.forEach(function (key) {
newArgs[key.toLowerCase()] = args[key]
})
var string = ''
for (var k in newArgs) {
string += '&' + k + '=' + newArgs[k]
}
string = string.substr(1)
return string
}
var ret = {
jsapi_ticket: ticket,//上个步骤中获取到的 jsapi_ticket。
nonceStr: createNonceStr(),
timestamp: createTimestamp(),
url:'www.url.com',//当前网页的URL。
};
var string = raw(ret);
const signature = sha1(string);//这就是咱们须要的签名。
复制代码
在页面中引入 JS 文件:
<script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
复制代码
权限验证(所需参数应向后端请求获得):
//前端 JavaScript 代码:
wx.config({
debug: true, // 开启调试模式。
appId: '', // 必填,APPID
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,上个步骤中生成的签名
jsApiList: ['openLocation'] // 必填,须要使用的JS接口列表,此处为地理位置接口 openLocation。
});
复制代码
监听点击事件:
qq.maps.event.addListener(marker, 'click', function () {
//判断运行设备是移动设备仍是桌面设备
if ((navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
))) {
//移动设备中调用 JSSDK 接口:
wx.openLocation({
latitude: 39.908551,
longitude: 116.397743,
name: '天安门',
address: '北京市东城区东长安街',
scale: 15, // 地图缩放级别,范围为从1~28。
});
} else {
//桌面设备中打开新网页展现地图:
window.open('https://j.map.baidu.com/26/BK');
}
});
复制代码
这一整套都操练完后,总算是实现了地图导航功能。
可能各位看官读到这里已经和我同样累了 —— 费这么大劲,作出来一个稀松日常的静态页面,真的值得吗?
固然不止如此。
DIY 电子请柬的念头刚刚萌生之时,我就下定决心要赋予这份请柬一个亮点:嘉宾姓名可定制。
想一想过年时你收到的群发短信,礼貌有余而诚意稍欠。你知道文字内容都是对方复制粘贴来的,因此那一行行气氛热烈的拜年话,你压根不会仔细看。
而我要作的,是让每一位嘉宾在请柬里看到本身的名字,让嘉宾明白:这封请柬是专属于你的,跟别人的都不同。
这是我向受邀亲友表达敬爱的方式,也是我为了让本身的婚礼尽可能不流于形式而作出的挣扎。
我将名字分为三部分:
实现思路是这样的:
将嘉宾名称做为请求参数拼接到 URL 的末尾,例如:
https://www.a.com?prefix=尊敬的&name=王大锤&suffix=先生
复制代码
在页面中用 JavaScript 获取到参数,动态插入到对应位置。
const info = window.location.search;
if (info.length) {
const arr = info.replace(/\?/, '').split('&');
const prefix = (arr[0].split('='))[1];
const name = (arr[1].split('='))[1];
const suffix = (arr[2].split('='))[1];
$('#prefix').html(decodeURI(prefix))
$('#name').html(decodeURI(name))
$('#suffix').html(decodeURI(suffix))
}
复制代码
这样,只要把对应的称呼拼到连接后面发给嘉宾,对方打开看到的就是内容专属的请柬了。
在前文中字体压缩的部分里,我提到了没有压缩
FZJunHJW_Cu.TTF
这个字体文件。读到这里你应该知道缘由了 —— 自定义称呼所用的汉字是不肯定的,因此只能加载完整的字体包。
但是若是给每一个嘉宾发请柬时都手动去拼连接,效率低不说,还容易出错。若是真这么整,我大概会被业界开除吧。
Don't repeat yourself.
解决这一需求的工具并不复杂:输入端是可变的文字,输出端是一个 URL。为了将懒惰发挥到极致,我还增长了一键复制功能和生成短网址。 页面实现很是简单 —— VUE + ElementUI 快速出货:
在我当时试用的两三个免费的短网址服务中,只有百度短网址支持 生成 https 短网址,这样更保险一些。七月份的时候,百度短网址仍是不收费的,如今变为收费模式了,不过生成短网址的速度提高了不少。
整个请柬到这里就全线贯通了,通过简单的测试和优化,就能够投入使用了。
在距离婚礼还有两个月的一个阳光明媚的上午,我心情舒畅地登陆微信,哼着小曲儿打开嘉宾名单,行云流水般地输入姓名、生成网址、发送请柬,享受着亲友们的称赞和祝福,感受本身牛逼坏了。
忽然!
我懵了。
明明是人畜无害的静态页面,咋就被定性为洪水猛兽了呢?
冷静冷静。即便再离谱的表象,其背后一定都有合乎逻辑的原因存在。不是说咱页面包含那个还有那个么,先从这个方向着手。我找到了一款 Chrome 插件,能够检测页面违规内容:
只要打开待检测页面,再点击 Chrome 右上角的 🚫 logo,该插件会在一个弹窗中列出页面中出现的嫌疑词,并在对应位置用明黄底色标记出来。以掘金首页为例,效果以下:
那么个人请柬页面中有哪些敏感词呢?
我可去你的吧。
我想微信所用的文本审核词库与插件的词库虽不彻底相同但也必有重叠,我又用肉眼反复排查了文案和代码,也没有发现可疑的词汇。
我猜是由于我短期内频繁地给多个用户发送连接(并且连接还带有参数),触发了微信的防护机制,将个人行为判定为骚扰行为之类的异常操做,因此将个人请柬封禁了。
好冤好委屈啊。别无他法,我点击微信拦截页面底部的 申请恢复访问
,填写相关信息后提交,就开始了充满焦躁和挫败感的等待过程。
过了不到半小时……
我着急啊,我上火啊,我一缕一缕地揪头发啊……
我又从微信外部连接内容管理规范中找到了申诉邮箱,诚诚恳恳地写了一封血泪之书寄了过去。对方很快回复我说已经解封,我测试了,确实能正常访问了。总算雨过天晴了,我决定中午多吃几碗抒发一下愉悦的心情。
饭后发现,又被封了…… 这简直让我想起了被微信小程序审核不经过支配的恐惧。
总之,通过了反反复复的被封 — 解封、再被封 — 再解封后,个人请柬总算再也不被认为是十八禁内容了。那两天个人人生就像过山车通常大起大落、上蹿下跳,安全落地后仍然心悸不已。
好了,各类尺寸屏幕前的各位观众朋友们,这就是我 DIY 电子请柬的全过程。技术含量不高,但曲折琐碎,固然乐趣和成就感也与投入成正比。
讲了这么多,最想和你们分享的就是,会写代码是很了不得的事情。
从笨拙地敲出 Hello, world
开始,咱们学习语言语法,探索各类库与框架,钻研算法…… 咱们会走得很远,咱们将冲在最前。
可能咱们都终日为工做所累,最初对编程那份最真挚的热爱和憧憬慢慢地淡了。
但请别忘了,编程是一种魔法,它能够解决工做中的问题,也一样能给你的生活添加不同的色彩。也许只是会一点 HTML,就能作出本身想要的请柬。
咱们都坚信,编程能给咱们带来无穷的乐趣,这也就是为何你在读这篇文章。
感谢你们的耐心阅读,若能针对文中的技术点指教一二,我将感激涕零;也欢迎遇到相同难题的掘友来和我讨论。
最后,祝你们都能和本身爱的人长相厮守、比翼连枝。