声明
本着技术为大的态度分享此文,仅仅作为技术分享,无任何引流和违规内容,请草率观看。
事情起因
在工作水群之余,看到某微信群里分享了一个小程序,一个可以调用手机马达让手机震动,可以设置震动节奏。到这里大家应该知道是什么类型的小程序了。(此处有狗头)
然后我在群里说了句,单机版有什么意思?
群友A:"别逼逼,那你出个联机版,出了算你赢。"
我:"好"。
男人这该死的胜负欲。
开发过程
注册/认证/备案小程序
注册流程不用说了,很简单。只是我这没有邮箱了,于是用了自己的域名绑定的腾讯企业邮箱搞定。
认证的话,个人认证 ¥30.00/年
,企业是 ¥300.00/年
。因为我啥接口和权限都不需要,就用了个人。
备案比较糟糕,填了资料,等了五天左右才下来。
开始开发
首先得来个 WebSocket服务端
,那就 NodeJS
吧。
javascript
console.log("\n**********************************");
const WSS_PORT = 10102;
const REDIS_PORT = 6379;
const REDIS_HOST = "127.0.0.1";
const websocket = require("nodejs-websocket");
const redis = require("ioredis");
const TOPIC_PREFIX = "topic_";
async function init() {
const webSocketServer = await websocket.createServer(async conn => {
console.log("[debug]\t\tConnection ready");
const redisSub = redis.createClient();
const redisPub = redis.createClient();
redisSub.subscribe("all");
redisSub.on("message", (channel, message) => {
console.log("channel,message", channel, message);
if (conn.readyState === 1) {
conn.send(message);
}
});
conn.on("text", async msg => {
console.log("[message]\t\t" + msg);
switch (msg) {
case "ping":
conn.send('pong');
break;
case "bye":
conn.send("bye");
conn.close();
break;
default:
try {
const obj = JSON.parse(msg);
switch (obj.type) {
case "join":
console.log("join " + obj.topic);
redisSub.subscribe(TOPIC_PREFIX + obj.topic);
break;
case "active":
try {
redisPub.publish(TOPIC_PREFIX + obj.topic, JSON.stringify({
type:"active",
msg: obj.msg
}));
} catch (e) {
console.error("发布失败");
}
break;
case "leave":
console.log("leave " + obj.topic);
redisSub.unsubscribe(TOPIC_PREFIX + obj.topic);
break;
case "touch":
try {
redisPub.publish(TOPIC_PREFIX + obj.topic, "touch");
} catch (e) {
console.error("发布失败");
}
break;
default:
console.log("unknown command: ".obj.type);
}
} catch (e) {
console.error(e);
}
}
});
conn.on("close", () => {
redisSub.quit();
});
conn.on("error", () => {});
});
webSocketServer.listen(WSS_PORT);
console.log("BBBUG Websocket Service Running : (" + WSS_PORT + ")");
}
init();
代码放出来了,因为做的仓促,没有加权限验证什么的,数据结构也是随心搞了搞,强迫症可以跳过代码质量这一趴。
客户端的话,我本来用的 TypeScript
写的小程序,但后来好像没什么需求,删了其他的文件之后,TypeScript
的优势一点都没体现出来。
代码如下:
typescript
const websocketUrl = "wss://hamm.cn/"
const apiUrl = "https://hamm.cn/"
Page({
data: {
shake: "",
isShaking: false,
isTouching: false,
ticket: "",
connected: false,
currentMessage: "",
messageList: [],
timerShake: -1,
timerShowMessage: -1,
timerMessageStop: -1,
timerHeartBeat: -1,
websocket: wx.connectSocket({
url: websocketUrl
}),
},
onLoad(option: any) {
let ticket = wx.getStorageSync("channel") || ""
if (option && option.ticket) {
ticket = option.ticket
}
this.setTicket(ticket)
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate(() => { })
updateManager.onUpdateReady(() => {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success: (res) => {
if (res.confirm) {
updateManager.applyUpdate()
}
}
})
})
updateManager.onUpdateFailed(function () {
})
},
getMessageList() {
wx.request({
url: apiUrl + "getMessageList",
success: (res) => {
const json = res.data as Record<string, any>
this.setData({
messageList: json.data
})
}
})
},
onShow() {
this.connect()
},
startHeartBeat() {
clearTimeout(this.data.timerHeartBeat)
console.log("心跳");
this.sendMessage("ping")
this.data.timerHeartBeat = setTimeout(() => {
this.startHeartBeat()
}, 10000);
},
sendMessage(res: string) {
try {
this.data.websocket.send({
data: res
})
} finally {
}
},
onShareAppMessage() {
return {
title: '快来一起震一下呀~',
path: '/view/index/index?ticket=' + this.data.ticket
}
},
joinChannel() {
wx.showModal({
title: "加入频道",
editable: true,
confirmText: "加入",
placeholderText: "请输入6位频道ID...",
success: (res) => {
if (res.confirm) {
if (!/^\d{6}$/.test(res.content)) {
wx.showModal({
title: "格式错误",
content: "请输入六位数字频道ID",
confirmText: "重新输入",
success: (res) => {
if (res.confirm) {
this.joinChannel();
}
}
})
return;
}
wx.showModal({
title: "加入频道",
content: "加入频道 " + res.content + " 成功!",
showCancel: false
})
this.setTicket(res.content)
this.getMessageList()
this.sendMessage(JSON.stringify({
type: "join",
topic: this.data.ticket
}))
}
}
})
},
stopShake() {
this.data.isShaking = false
this.setData({
shake: "."
})
},
exitChannel() {
wx.showModal({
title: "退出频道",
content: "是否确认退出频道 " + this.data.ticket,
confirmText: "退出频道",
confirmColor: "#f00",
success: (res) => {
if (res.confirm) {
this.stopShake()
this.sendMessage(JSON.stringify({
type: "leave",
topic: this.data.ticket
}))
this.setTicket("")
}
}
})
},
setTicket(ticket: string) {
this.setData({
ticket: ticket
})
wx.setStorageSync("channel", ticket)
},
connect() {
this.data.websocket.onOpen(() => {
this.data.connected = true
wx.showToast({
title: "连接成功",
icon: "success"
})
if (this.data.ticket) {
this.sendMessage(JSON.stringify({
type: "join",
topic: this.data.ticket
}))
}
this.startHeartBeat()
});
this.data.websocket.onMessage((res) => {
switch (res.data) {
case "pong":
break;
case "touch":
if (this.data.isTouching) {
return
}
this.startShake()
clearTimeout(this.data.timerMessageStop)
this.data.timerMessageStop = setTimeout(() => {
this.stopShake()
}, 500)
break;
default:
try {
const json = JSON.parse(res.data.toString());
switch (json.type) {
case 'active':
this.setData({
currentMessage: json.msg || ""
})
clearTimeout(this.data.timerShowMessage)
this.data.timerShowMessage = setTimeout(() => {
this.setData({
currentMessage: ""
})
}, 3000)
break;
default:
}
} catch (e: any) {
console.log(e);
}
}
})
this.data.websocket.onClose(() => {
clearTimeout(this.data.timerHeartBeat)
this.data.connected = false
wx.showToast({
title: "连接断开",
icon: "error"
})
this.data.websocket = wx.connectSocket({
url: websocketUrl
})
})
},
createConnection() {
wx.showToast({
title: "连接中",
icon: "loading"
})
this.connect()
},
publish() {
if (this.data.ticket && this.data.isTouching) {
this.sendMessage(JSON.stringify({
type: "touch",
topic: this.data.ticket
}))
console.log("touch");
}
},
touchStart() {
console.log("开始震动")
this.data.isTouching = true;
this.startShake()
},
startShake() {
if (!this.data.isShaking) {
this.data.isShaking = true
this.setData({
shake: "shake"
})
this.shake()
}
},
touchStop() {
this.data.isTouching = false
this.stopShake();
this.setData({
shake: "."
})
console.log("停止震动")
},
shake() {
if (!this.data.isShaking) {
return
}
console.log("...");
this.publish()
wx.vibrateShort({
type: "heavy"
})
setTimeout(() => {
wx.vibrateLong()
setTimeout(() => {
wx.vibrateShort({
type: "heavy"
})
this.data.timerShake = setTimeout(() => {
this.shake()
}, 100)
}, 410)
}, 30)
},
fastMessage() {
wx.showActionSheet({
itemList: this.data.messageList,
success: (res) => {
this.sendMessage(JSON.stringify({
type: "active",
topic: this.data.ticket,
msg: this.data.messageList[res.tapIndex]
}))
}
})
},
})
如上,完成了 单机版 + 独立频道联机版。就这么发布吧。
然后就分享到了一些微信群,其中就有 掘金前端交流群,然后就被踢了哈哈哈哈哈哈哈哈哈哈哈哈。
后续
可能会跟一些支持蓝牙的 硬件 互动一下(手动狗头),有兴趣的朋友可以继续关注我后续的文。
最后
可以搜索一下 震当时 体验一下,本文只是做一个开发趣事分享,就不贴二维码引流了。
就酱。