halo,大家好,我是 132,今天是洗马八周年生日,然后我昨天加班到两点,就是要上线庆生活动页
预览地址:m.ximalaya.com/web-support…
看上去是个很简单的的地址,简单的切图,然后几个接口请求完事,但其实涉及到的东西还是很多的,容我一一道来
评论框光标
点击一个表情,会在当前光标处,加上去一个[文字]的纯文本,然后再次focus 的时候,要不改变光标定位
大家如果用 antd 啥的应该内部都有处理,我是原生的,只能自己写
const content = (str: string) => {
var tc = document.getElementById("input");
var len = tc.value.length;
let pos = len;
tc.focus();
if (typeof document.selection != "undefined") {
document.selection.createRange().text = str;
} else {
pos = tc.selectionStart + str.length;
tc.value =
tc.value.substr(0, tc.selectionStart) +
str +
tc.value.substring(tc.selectionStart, len);
}
tc.setSelectionRange(pos, pos);
setValue(tc.value);
};
大概需要这样处理,focus 之后,光标会移动到最后或者前面,然后 setSelectionRange 用来重新定位
微信站外跳转
export function openApp() {
const originUrl = "iting://open?msg_type=14&url=http://m.ximalaya.com/web-support/api/layout/8-years";
const itingurl = encodeURIComponent(originUrl);
let pathUrl = "open_xm=" + itingurl + "&android_schema=" + itingurl;
setTimeout(() => {
window.location.href = "http://m.ximalaya.com/down?" + pathUrl;
}, 2000);
}
普通的 url 跳转,在微信里面,大概率是【复制到浏览器打开】,所以这里用跳转应用宝的方法,然后应用宝中间唤起
当然如果用户没有装应用宝,这个方法还是不行的
简单封装 fetch
export function post(url, data) {
return new Promise((resolve, reject) => {
fetch(url, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
credentials: "include"
})
.then((res) => res.json())
.then((data) => {
resolve(data);
})
.catch((e) => {
reject(e);
});
});
}
export function get(url) {
return new Promise((resolve, reject) => {
fetch(url, {
credentials: "include"
})
.then((res) => res.json())
.then((data) => {
resolve(data);
})
.catch((e) => {
reject(e);
});
});
}
很简单的两个小方法的封装,从此摆脱 axios,注意 credentials 这个字段可以解决一些 cookie 问题
h5 视频小窗播放
默认情况下,ios 的视频会直接唤起全屏,需要家 playsinline
<video
src="1.mp4"
controls
poster="/1.png"
controlsList="nodownload"
webkit-playsinline="true"
x5-playsinline="true"
x-webkit-airplay="allow"
playsInline
></video>
移动端适配问题
以前没有写过活动页,说实话我对移动端适配是没有概念的,所以这次我用的是 px 方案 但是我发现………………因为设计稿是切图的,用 px 方案根本无法对图片进行拉伸,导致各种错位
为了能够调整尺寸,我写了大量的 js 逻辑去计算尺寸
const video = document.querySelector(".video") as any;
const v = document.querySelector("video") as any;
const w = document.body.clientWidth;
video.style.width = w - 20 + "px";
video.style.height = (445 / 700) * video.clientWidth + "px";
v.style.width = video.clientWidth - 20 + "px";
v.style.height = v.clientWidth/16*9 + "px";
// 修改列表样式
const items = document.querySelectorAll(".item") as any;
for (let i = 0; i < items.length; i++) {
const item = items[i];
item.style.width = w / 2 - 17 + "px";
item.style.height = (309 / 340) * item.clientWidth + "px";
}
是不是很疯狂,但也没有办法,和设计稿也有关,和手机分辨率也有关
不得不承认,rem 这种成倍拉伸,仍然是活动页的最佳方案
历史上有个淘宝的 flexiable.js 方案,但很臃肿,我们完全可以简单实现一下:
(function(designWidth, maxWidth) {
var doc = document,
win = window,
docEl = doc.documentElement,
remStyle = document.createElement("style"),
tid;
function refreshRem() {
var width = docEl.getBoundingClientRect().width;
maxWidth = maxWidth || 540;
width>maxWidth && (width=maxWidth);
var rem = width * 100 / designWidth;
remStyle.innerHTML = 'html{font-size:' + rem + 'px;}';
}
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(remStyle);
} else {
var wrap = doc.createElement("div");
wrap.appendChild(remStyle);
doc.write(wrap.innerHTML);
wrap = null;
}
refreshRem();
win.addEventListener("resize", function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener("pageshow", function(e) {
if (e.persisted) {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
if (doc.readyState === "complete") {
doc.body.style.fontSize = "16px";
} else {
doc.addEventListener("DOMContentLoaded", function(e) {
doc.body.style.fontSize = "16px";
}, false);
}
})(750, 750);
不要问我为什么不用 flex 布局,这是弹性布局,活动页很明显不够弹
微信二次分享
是的你没看错,昨天加班就是这个需求,我是很绝望的
所谓二次分享,就是你把链接分享到微信,然后再从微信分享到好友,然后它的描述啥的是有样式的
先引用 微信 sdk
<script src="https://s1.xmcdn.com/wap/js/lib/weixin.js" async></script>
然后重点在于发两个请求
import {get,post} from '../api/index'
const wx = window.wx || {}
let count = 0;
export const wxInit = () => {
const thirdpartyId = 17;
const host = "https://passport.ximalaya.com";
return get(`${host}/xthirdparty-toolkit-web/wechat/jssdk/config/${thirdpartyId}?signatureUrl=${encodeURIComponent(window.location.href.split('#')[0])}&_=${+new Date()}`,
);
};
function share(sData) {
const thirdpartyId =17;
return wxInit(thirdpartyId, sData.link).then((data) => {
const config = {
debug: false,
appId: data.appId,
timestamp: data.timestamp,
nonceStr: data.nonceStr,
signature: data.signature,
jsApiList: [
"checkJsApi",
"onMenuShareTimeline",
"onMenuShareAppMessage",
"onMenuShareQQ",
"onMenuShareWeibo",
"hideMenuItems",
"showMenuItems",
"hideAllNonBaseMenuItem",
"showAllNonBaseMenuItem",
"chooseWXPay",
],
};
wx.config(config);
return new Promise((resolve) => {
wx.ready(() => {
console.log(sData)
if (sData) {
// 朋友圈
wx.onMenuShareTimeline(sData);
// 朋友
wx.onMenuShareAppMessage(sData);
// qq
wx.onMenuShareQQ(sData);
// weibo
wx.onMenuShareWeibo(sData);
wx.showAllNonBaseMenuItem();
}
resolve(0);
});
});
});
}
export async function WxShare(sData) {
const state = await _WxShare(sData);
if (state == 2) {
share(sData);
}
return state;
}
export const wxConfig = (thirdpartyId) => {
return get(`https://m.ximalaya.com/x-thirdparty-web/weixinJssdk/config?signatureUrl=${encodeURIComponent(window.location.href.split('#')[0])}&_=${+new Date()}&thirdpartyId=${thirdpartyId}`
);
};
上面是最小代码,然后重点是 encodeURIComponent(window.location.href.split('#')[0]) 需要经过两层处理,不然会失效
总结
以上分享的代码均可复用,大家可以根据自己的业务场景自己积累
不说了,加班太累,我先去躺会儿