洗马八周年活动页开发总结

1,151 阅读3分钟

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]) 需要经过两层处理,不然会失效

总结

以上分享的代码均可复用,大家可以根据自己的业务场景自己积累

不说了,加班太累,我先去躺会儿