程序員用Python給了女友一個(gè)七夕驚喜!
回復(fù)“書籍”即可獲贈(zèng)Python從入門到進(jìn)階共10本電子書

七夕(各種節(jié)日、紀(jì)念日)又快到啦,程序員(怎么會(huì)不是單身呢)又要想招來哄女友啦?
想必大家都知道各種各樣的代碼式浪漫,比如定制的二維碼,讓女友掃碼后進(jìn)入一個(gè)定制的 h5 頁面,那么這個(gè)頁面里可以放的內(nèi)容是——
回憶,是經(jīng)典的選項(xiàng)。該如何呈現(xiàn)回憶呢?
不難想到可以用 js 來實(shí)現(xiàn)各種動(dòng)畫效果,直接 copy 各種庫組合組合是不錯(cuò),但親力親為還是需要經(jīng)過精心設(shè)計(jì),操作起來有一定的難度。
這里給大家提供一個(gè)簡單的點(diǎn)子,用 python 來制作酷炫的動(dòng)態(tài)條形圖,展示你們?cè)谝黄鸬臍v程吧!
例子如下:

一、動(dòng)態(tài)條形圖
首先,不妨猜想一下這個(gè)是如何實(shí)現(xiàn)的。動(dòng)畫即是一幀一幀靜態(tài)畫面的連續(xù)播放,所以我們只需要將每一天都畫一次圖,再拼成 GIF 即可。
如下為第一天和最后一天的條形圖:


再來看一下用于畫圖的每日數(shù)據(jù),假設(shè)2020年1月1日為起始日期,1月20日為當(dāng)天(即發(fā)布供檢閱的)日期,故要對(duì)這些數(shù)據(jù)畫20次圖(別怕,兄dei)。

進(jìn)入代碼環(huán)節(jié):先按需求讀取數(shù)據(jù)(讀表最愛的 pandas 庫又出現(xiàn)啦)。為了便于處理日期,將 excel 中的日期一列的值轉(zhuǎn)為字符串格式,再利用 datatime 將起始日期設(shè)為時(shí)間戳格式。
import pandas as pd
import datetime
df = pd.read_excel("數(shù)據(jù).xlsx")
df['日期文本'] = df['日期'].apply(lambda x: str(x)[:10])
t = datetime.datetime(2020,1,1) # 起始日期
選擇 matplotlib 庫進(jìn)行繪圖:先設(shè)置畫布,返回模型和畫圖對(duì)象。接著不要忘記設(shè)置字體以避免中文顯示異常。因?yàn)橛?個(gè)項(xiàng)目需要區(qū)分上色,因此再創(chuàng)建一個(gè)顏色列表,可以自行百度喜歡的顏色代碼。
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(10,6)) # 畫布
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 字體設(shè)為微軟雅黑
colors = ['#ADD8E6', '#DC143C', '#FFC0CB'] # 顏色列表
編寫繪圖函數(shù):傳入的參數(shù)是對(duì)于起始日期所經(jīng)過的天數(shù)。通過 t + datetime.timedelta(days=date) 計(jì)算需要繪制的指定天數(shù)的日期,再利用 strftime("%Y-%m-%d") 將其還原為日期文本,然后通過該日期文本取出當(dāng)天的數(shù)據(jù)存入新的 df_ 中。下一步即為通過 barh 方法繪制條形圖,且每次畫新圖前需清空上一次的圖像。
def draw(date):
# 數(shù)據(jù)處理 ------
current_date = (t + datetime.timedelta(days=date)).strftime("%Y-%m-%d")
df_ = df[df['日期文本'].eq(current_date)]
days = df_['天數(shù)']
item = df_["項(xiàng)目"]
# 繪制條形圖 ------
ax.clear() # 重繪
# for i in range(1,len(itme.uni))
ax.barh(item, days, color = colors)
如此之后,調(diào)用 draw(19) 來畫出經(jīng)過19天后,也就是第20天的圖像,通過 plt.show() 臨時(shí)查看一下。

和最終效果圖還有一定的差距,多了坐標(biāo)軸標(biāo)簽,少了系列標(biāo)簽、數(shù)據(jù)標(biāo)注和右上角的滾動(dòng)時(shí)間。繼續(xù)完善 draw 函數(shù):
for y, (x,name) in enumerate(zip(days.values,item.values)): # 系列標(biāo)注
ax.text(x, y, "%s" % x, size=12)
if x > 1:
ax.text(x-0.5, y, name, size=14, ha = 'right')
ax.text(1, 1.01, current_date, transform = ax.transAxes, size= 20, ha='right') # 滾動(dòng)時(shí)間
ax.get_xaxis().set_visible(False) # 隱藏坐標(biāo)軸
ax.get_yaxis().set_visible(False)
接下來就是用 for 循環(huán)畫出20張圖并通過 plt.savefig('xxx.png') 一一保存,再使用 imageio 庫或其他圖像工具來合成 gif 啦!
(不不不,慢著慢著)如果真要這樣做就太麻煩了,下面該祭出這次的主角了!
import matplotlib.animation as ani
matplotlib 庫提供了動(dòng)態(tài)繪圖的模塊,可以幫助我們更加輕松的制作 gif。只需傳入模型、繪圖函數(shù)、和一個(gè) int 類型的列表即可,因此最初設(shè)計(jì) draw 函數(shù)時(shí)所需的參數(shù)是天數(shù) date。interval 參數(shù)為繪制每張圖的時(shí)間間隔,用于在 plt.show() 中檢查效果。最終保存 gif 圖像時(shí)可以通過 fps 參數(shù)設(shè)置幀數(shù)。
timeSlot = [x for x in range(0,20)] # 時(shí)間軸
animator = ani.FuncAnimation(fig, draw, frames=timeSlot ,interval = 100)
animator.save('test.gif',fps=10)
附完整代碼:
import matplotlib.pyplot as plt
import matplotlib.animation as ani
import pandas as pd
import datetime
df = pd.read_excel("數(shù)據(jù).xlsx")
df['日期文本'] = df['日期'].apply(lambda x: str(x)[:10])
t = datetime.datetime(2020,1,1) # 起始日期
fig, ax = plt.subplots(figsize=(10,6)) # 畫布
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 字體設(shè)為微軟雅黑
timeSlot = [x for x in range(0,20)] # 時(shí)間軸
colors = ['#ADD8E6', '#DC143C', '#FFC0CB'] # 顏色列表
def draw(date):
print(date)
# 數(shù)據(jù)處理 ------
current_date = (t + datetime.timedelta(days=date)).strftime("%Y-%m-%d")
df_ = df[df['日期文本'].eq(current_date)]
days = df_['天數(shù)']
item = df_["項(xiàng)目"]
# 繪制條形圖 ------
ax.clear() # 重繪
ax.barh(item, days, color = colors)
for y, (x,name) in enumerate(zip(days.values,item.values)): # 系列標(biāo)注
ax.text(x, y, "%s" % x, size=12)
if x > 1:
ax.text(x-0.5, y, name, size=14, ha = 'right')
ax.text(1, 1.01, current_date, transform = ax.transAxes, size= 20, ha='right') # 滾動(dòng)時(shí)間
ax.get_xaxis().set_visible(False) # 隱藏坐標(biāo)軸
ax.get_yaxis().set_visible(False)
# draw(19)
# plt.savefig('test.png')
animator = ani.FuncAnimation(fig, draw, frames=timeSlot ,interval = 100) # interval時(shí)間間隔
plt.show()
# animator.save('test.gif',fps=10)
二、定制二維碼
不解釋!(看注釋)直接上代碼:
from MyQR import myqr # 需先安裝MyQR庫
def QR_myqr():
myqr.run(
'https://', # 二維碼指向鏈接,或無格式文本(但不支持中文)
version = 5, # 大小1~40
level='H', # 糾錯(cuò)級(jí)別
picture = 'img.jpg', # 底圖路徑
colorized = True, # 彩色
contrast = 1.0, # 對(duì)比度
brightness = 1.0, # 亮度
save_name = 'save.jpg', # 保存文件名
save_dir = 'D:/' #保存目錄
)
三、編寫靜態(tài)html頁面
如果需要通過二維碼來訪問你的網(wǎng)站,那就需要先將其部署到服務(wù)器,方法也是多種多樣的,比如某企鵝云,個(gè)人用戶有6個(gè)月的免費(fèi)時(shí)常。我們?cè)谶@里要介紹的是 github(其實(shí)是因?yàn)槲夜镜碾娔X不能上外網(wǎng),測試的時(shí)候用不了企鵝云才用的 github,國內(nèi)手機(jī)訪問還是放在國內(nèi)的服務(wù)器比較快,大概是的)
(嗯?就算你問上不了外網(wǎng)卻能上 github 我也…大概是限制的網(wǎng)段沒覆蓋到吧哈哈)
(嗯?我在公司劃水的事情暴露了嗎)。
不過在那之前,先把本地的 html 寫好吧!
通過開頭的最終(不是最終的)效果圖可以發(fā)現(xiàn),gif 是首尾相接循環(huán)播放的,那最后一天的圖像一下子閃過去就看不清楚了,可以修改一下傳入的時(shí)間序列,把最后一幅圖再畫多幾遍,就有停留的效果了。為了更好地展現(xiàn)效果,下面的圖中所用數(shù)據(jù)的時(shí)間周期改為了從6月1日到8月25日(七夕),經(jīng)過了86天,并增加了兩條項(xiàng)目。
timeSlot = [x for x in range(0,86)]+[85]*15

直接放進(jìn) html 頁面里,就單單一張圖好像還缺了點(diǎn)什么,那就跟隨動(dòng)圖的節(jié)奏在下方打印文字吧。首先設(shè)置兩個(gè) div 的樣式,一個(gè)用于展示 gif,一個(gè)用于打印文字:
<head>
<style>
.process_gif{ /*顯示動(dòng)態(tài)barh*/
background-image:url("./process.gif");
background-repeat: no-repeat;
background-size: cover;
margin:0 auto;
width: 370px;
height: 220px;
position: relative;
z-index: 1;
}
.show_txt{ /*顯示文字*/
margin:0 auto;
background-color: azure;
width: 370px;
height: 200px;
position: relative;
text-align: center;
padding-top: 10px;
z-index: 1;
}
</style>
</head>
然后在 body 里讓它們顯示出來:
<body>
<div class="process_gif" id="process"></div>
<div class="show_txt" id="content_1"></div>
</body>

編寫 js 腳本實(shí)現(xiàn)打印功能,在頁面加載時(shí)就調(diào)用打印函數(shù) typing,并且在動(dòng)態(tài)圖播放到最后一幅時(shí),將其替換成靜態(tài)圖:
<script type="text/javascript">
/// 顯示文字功能 ----------------------------------
let divTyping = document.getElementById('content_1'); //通過id獲取div節(jié)點(diǎn)
let a = 0;
timer = 0;
str = "我們已經(jīng)相遇 20 天<br>告白后過了 13 天<br>First Kiss 至今 5 天";
function typing () {
if (a <= str.length) { # 從第一個(gè)字開始逐個(gè)打印
divTyping.innerHTML = str.slice(0, a++) + '_';
timer = setTimeout(typing, 50); # 設(shè)置打印時(shí)間間隔
}
else {
divTyping.innerHTML = str; //結(jié)束打字,移除 _ 光標(biāo)
clearTimeout(timer);
}
}
window.onload=function(){
typing();
setTimeout(function(){
thisdiv = document.getElementById("process");
thisdiv.style.backgroundImage = "url('./process_stop.png')"; # 將div背景圖替換
},2000); # 單位是毫秒,根據(jù)動(dòng)態(tài)圖的時(shí)長來設(shè)置
}
</script>
來看一下,真的是真的.真.最終效果圖:

注意: 動(dòng)圖的時(shí)長和幀數(shù),以及動(dòng)圖在html中與逐行打印文字同步顯示,大家還需根據(jù)實(shí)際內(nèi)容對(duì)代碼進(jìn)行調(diào)整,以達(dá)到最佳效果哦!
好了不想寫了,快速部署的部分大家自己搜索資料吧... ...

------------------- End -------------------
往期精彩文章推薦:

歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持
想加入Python學(xué)習(xí)群請(qǐng)?jiān)诤笈_(tái)回復(fù)【入群】
萬水千山總是情,點(diǎn)個(gè)【在看】行不行
/今日留言主題/
隨便說一兩句吧~~
