本文首發於掘金,轉載請註明出處,保留原文連接以及做者信息。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,就能作出本身想要的請柬。
咱們都堅信,編程能給咱們帶來無窮的樂趣,這也就是爲何你在讀這篇文章。
感謝你們的耐心閱讀,若能針對文中的技術點指教一二,我將感激涕零;也歡迎遇到相同難題的掘友來和我討論。
最後,祝你們都能和本身愛的人長相廝守、比翼連枝。