[TOC]
# 功能
## buffer-queue
```
site/wechat-react/components/tools/buffer-queue.js
```
## socket-handle
```
site/wechat-react/components/tools/ql-socket-handle.js
```
## 獲取滑動方向
```javascript
//獲取滑動方向
var getScrollDirection = function() {
//返回角度
function GetSlideAngle(dx,dy) {
return Math.atan2(dy,dx) * 180 / Math.PI;
}
//根據起點和終點返回方向
function GetSlideDirection(startX,startY, endX, endY) {
var dy = startY - endY;
var dx = endX - startX;
var result = 0;
//如果滑動距離太短
if (Math.abs(dx) < 2 && Math.abs(dy) < 2) {
return result;
}
//45到135 向上滑動 -180到0向下滑動
var angle = GetSlideAngle(dx, dy);
if (angle >= 45 && angle < 135) {
$('.nav').fadeOut();
}else if(angle >= -135 && angle < -45){
$('.nav').fadeIn();
}
}
//滑動處理
var startX, startY;
document.addEventListener('touchstart', function (ev){
startX = ev.touches[0].pageX;
startY = ev.touches[0].pageY;
}, false);
document.addEventListener('touchmove', function (ev){
var endX, endY;
endX = ev.changedTouches[0].pageX;
endY = ev.changedTouches[0].pageY;
GetSlideDirection(startX, startY, endX, endY);
}, false);
}
```
## 圖片縮放
```javascript
/*
* 縮放、居中圖片
* image [dom] jquery元素
* scaleWidth [number] 縮放后的寬
* scaleHeight [number] 縮放后的高
* middle [boolean] 是否居中
*
*/
function imgScale(image, scaleWidth, scaleHeight, middle) {
//獲取圖片的原始寬高和比例
var originWidth = image[0].naturalWidth || image[0].width;
var originHeight = image[0].naturalHeight || image[0].height;
var ratio = originWidth / originHeight;
//如果原圖寬比高大
if(ratio > 1) {
//如果縮放后的寬度比要求的寬度要小,把寬度縮放為要求的寬度
if(originWidth/(originHeight/scaleHeight) < scaleWidth) {
image.width(scaleWidth);
//否則把圖片的高度縮放為要求的高度
}else {
image.height(scaleHeight);
}
} else {
if(originHeight/(originWidth/scaleWidth) < scaleHeight) {
image.height(scaleHeight);
}else {
image.width(scaleWidth);
}
}
//判斷是否需要居中
if(middle) {
var marginLeft = (originWidth/(originHeight/scaleHeight) - scaleWidth) / 2;
var marginTop = (originHeight/(originWidth/scaleWidth) - scaleHeight) / 2;
ratio > 1 ? image.css('marginLeft', -marginLeft) : image.css('marginTop', -marginTop);
}
}
```
## 實時統計input輸入內容
```javascript
var el = document.getElementById('element')
el.addEventListener('compositionstart', onCompositionStart);
el.addEventListener('compositionend', onCompositionEnd);
function onCompositionStart (e) {
e.target.composing = true;
}
function onCompositionEnd (e) {
e.target.composing = false;
trigger(e.target, 'input');
}
function trigger (el, type) {
var e = document.createEvent('HTMLEvents');
e.initEvent(type, true, true);
el.dispatchEvent(e);
}
el.addEventListener('input', function(e){
if(!e.target.composing) {
console.log(this.value);
}
});
```
## WAP自動播放音樂
微信通過監聽特定的事件可實現自動播放,UC瀏覽器不支持自動播放,IOS、安卓需要用戶交互后才能播放音樂
```javascript
var music = document.getElementById('audio');
//微信
document.addEventListener("WeixinJSBridgeReady", function () {
music.play();
}, false);
//IOS、安卓
$(document).on('touchstart', function() {
music.load()
music.play();
})
```
## 展開收起
```
// 文本內容
export const ContentText = memo(({ text, maxLine = 3 }) => {
if (!text) {
return null;
}
const [showAll, setShowAll] = useState(false);
const [showControlBtn, setShowControlBtn] = useState(false);
const ctnRef = useRef();
useEffect(() => {
if (text && ctnRef.current) {
if (ctnRef.current.scrollHeight > ctnRef.current.offsetHeight) {
setShowControlBtn(true);
} else {
setShowControlBtn(false);
}
}
}, [text]);
// 展開
const onSpread = useCallback((e) => {
e.stopPropagation();
setShowAll(true);
}, []);
// 收起
const onClose = useCallback((e) => {
e.stopPropagation();
setShowAll(false);
}, []);
return (
<div>
<p
ref={ctnRef}
className={`content-text`}
style={!showAll ? { WebkitLineClamp: maxLine } : null}
dangerouslySetInnerHTML={{ __html: text }}
></p>
{showControlBtn ? (
<div>
{showAll ? (
<span onClick={onClose}>
收起
</span>
) : (
<span onClick={onSpread}>
展開
</span>
)}
</div>
): null}
</div>
);
});
```
```css
.content-text {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
word-break: break-all;
}
```
## 固定按鈕
~~~ css
.fans-intro-distribution-btn {
position: absolute;
right: 0;
bottom: 270px;
display: flex;
align-items: center;
padding: 10px 20px 15px;
background-color: #fff7db;
border-radius: 100px 0px 0px 100px;
transform: translateX(0px);
transition: transform 0.3s linear 1s;
&.scroll {
transition: opacity,transform 0.5s linear;
}
}
~~~
~~~ javascript
import React, { useEffect, useState, useRef, useCallback } from 'react';
let timer = null;
export function DistributionBtn ({
scrollNode,
handleClick,
}) {
const [isScroll, setIsScroll] = useState(false);
const [leftValue, setleftValue] = useState(0);
const spanRef = useRef();
const btnRef = useRef();
// 監聽滾動條
const handleScroll = useCallback(() => {
const node = typeof document != 'undefined' && document.querySelector(`.${scrollNode}`);
if (node) {
node.addEventListener('scroll', () => {
setIsScroll(true);
timer && clearTimeout(timer);
timer = setTimeout(()=>{
setIsScroll(false);
}, 400)
})
}
})
const setTranslateValue = useCallback(() => {
var paddingLeft = Number(window.getComputedStyle(btnRef.current)['padding-right'].replace(/px/, ''));
var spanWidth = spanRef.current.clientWidth;
setleftValue(paddingLeft + spanWidth);
}, [btnRef])
useEffect(() => {
if ( !scrollNode) return;
handleScroll();
setTranslateValue();
return () => {
timer && clearTimeout(timer);
}
}, [scrollNode]);
return (
<div
className={`fans-intro-distribution-btn ${isScroll ? 'scroll' : ''}`}
style={{
transform: isScroll ? [`translateX(${leftValue}px)`] : 'none',
}}
ref={btnRef}
onClick={handleClick}
>
<i className="red-packet"></i>
<span ref={spanRef}>分享賺0元</span>
<i className="distribution-bg"></i>
</div>
)
}
~~~
## 獲取上傳視頻封面
~~~
// 選擇視頻
handleChooseVideo() {
//...
let file = this.$refs.chooseVideo.files[0]
if(!file) return
let size = Math.floor(file.size / 1024)
if(size > 3*1024) {
alert('請選擇3MB以內的視頻')
return false
}
// 獲取視頻地址
let videoUrl
if(window.createObjectURL != undefined) {
videoUrl = window.createObjectURL(file)
} else if (window.URL != undefined) {
videoUrl = window.URL.createObjectURL(file)
} else if (window.webkitURL != undefined) {
videoUrl = window.webkitURL.createObjectURL(file)
}
let $video = document.createElement('video')
$video.src = videoUrl
// 防止移動端封面黑屏或透明白屏
$video.play()
$video.muted = true
$video.addEventListener('timeupdate', () => {
if($video.currentTime > .1) {
$video.pause()
}
})
// 截取視頻第一幀作為封面
$video.addEventListener('loadeddata', function() {
setTimeout(() => {
var canvas = document.createElement('canvas')
canvas.width = $video.videoWidth * .8
canvas.height = $video.videoHeight * .8
canvas.getContext('2d').drawImage($video, 0, 0, canvas.width, canvas.height)
let videoThumb = canvas.toDataURL('image/png')
console.log(videoThumb)
}, 16);
})
},
~~~
## 讓一個scroll的容器跳轉到底部
~~~
export const scrollToBottom = function (scrollContainer) {
const scrollY = scrollContainer.scrollHeight,
scrollX = scrollContainer.scrollLeft;
scrollContainer.scroll(scrollX, scrollY);
}
~~~
~~~
const inputRef = React.useRef(null);
// 初始化input
const initEl = React.useCallback((el) => {
if (el) {
inputRef.current = el;
el.focus();
}
}, []);
const handleFoucs = React.useCallback(() => {
if (inputRef.current) {
scrollToBottom(document.documentElement);
}
}, []);
<textarea
ref={initEl}
onFocus={handleFoucs}
/>
~~~
## iOS、安卓視頻播放
~~~
<video
webkit-playsinline="true" // iOS10 設置視頻在小窗內播放
playsinline="true" // iOS微信瀏覽器支持小窗內播放
x-webkit-airplay="allow"
x5-video-player-type="h5" // 啟用H5播放器,wechat安卓版特性
x5-video-player-fullscreen="true" // 全屏設置
x5-video-orientatioon="portraint" // 播放器的方向,landscape橫屏,portraint豎屏,默認值為豎屏
style="object-fit: fill"
/ >
~~~
## 打開原生應用
~~~
<a href="weixin://">打開微信</a>
<a href="alipays://">打開支付寶</a>
<a href="alipays://platformapi/startapp?saId=10000007">打開支付寶的掃一掃功能</a>
<a href="alipays://platformapi/startapp?appId=60000002">打開支付寶的螞蟻森林</a>
~~~
這種方式叫做`URL Scheme`,是一種協議,一般用來訪問`APP`或者`APP`中的某個功能/頁面(如喚醒`APP`后打開指定頁面或者使用某些功能)??
`URL Scheme`的基本格式如下:
~~~
行為(應用的某個功能/頁面)
|
scheme://[path][?query]
| |
應用標識 功能需要的參數
復制代碼
~~~
一般是由`APP`開發者自己定義,比如規定一些`參數`或者`路徑`讓其他開發者來訪問,就像上面的例子??
注意事項:
* 喚醒`APP`的條件是你的手機已經安裝了該`APP`
* 某些瀏覽器會禁用此協議,比如微信內部瀏覽器(除非開了白名單)
## 解決input失焦后頁面沒有回彈

一般出現在`IOS設備中的微信內部瀏覽器`,出現的條件為:
* 頁面高度過小
* 聚焦時,頁面需要往上移動的時候
所以一般`input`在頁面上方或者頂部都不會出現無法回彈
解決辦法為,在聚焦時,獲取當前滾動條高度,然后失焦時,賦值之前獲取的高度:
~~~
<template>
<input type="text" @focus="focus" @blur="blur">
</template>
<script>
export default {
data() {
return {
scrollTop: 0
}
},
methods: {
focus() {
this.scrollTop = document.scrollingElement.scrollTop;
},
blur() {
document.scrollingElement.scrollTo(0, this.scrollTop);
}
}
}
</script>
~~~
## 滑動穿透
### 方法1
當在`遮罩`上滑動的時候,是會穿透到父節點的,最簡單的辦法就是阻住默認行為:
~~~
document.querySelector(".mask").addEventListener("touchmove", event => {
event.preventDefault();
});
~~~
如果在`vue`中,你可以這么寫:
~~~
<div class="mask" @touchumove.prevent></div>
~~~
如果`.content`也有滾動條,那么只要阻止`遮罩`本身就行:
~~~
document.querySelector(".mask").addEventListener("touchmove", event => {
if (event.target.classList.contains("mask")) event.preventDefault();
});
~~~
或者:
~~~
<div class="mask" @touchumove.self.prevent></div>
~~~
### 方法2
[參考鏈接(已失效)](https://uedsky.com/2016-06/mobile-modal-scroll/)
~~~javascript
ModalHelper: (function(bodyCls) {
var scrollTop
return {
afterOpen: function() {
scrollTop = document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop
$('body').addClass(bodyCls)
document.body.style.top = -scrollTop + 'px'
},
beforeClose: function() {
$('body').removeClass(bodyCls)
// scrollTop lost after set position:fixed, restore it back.
if (document.documentElement) {
document.documentElement.scrollTop = scrollTop
} else {
document.body.scrollTop = scrollTop
}
}
}
})('modal-open')
~~~
- 第一部分 HTML
- meta
- meta標簽
- HTML5
- 2.1 語義
- 2.2 通信
- 2.3 離線&存儲
- 2.4 多媒體
- 2.5 3D,圖像&效果
- 2.6 性能&集成
- 2.7 設備訪問
- SEO
- Canvas
- 壓縮圖片
- 制作圓角矩形
- 全局屬性
- 第二部分 CSS
- CSS原理
- 層疊上下文(stacking context)
- 外邊距合并
- 塊狀格式化上下文(BFC)
- 盒模型
- important
- 樣式繼承
- 層疊
- 屬性值處理流程
- 分辨率
- 視口
- CSS API
- grid(未完成)
- flex
- 選擇器
- 3D
- Matrix
- AT規則
- line-height 和 vertical-align
- CSS技術
- 居中
- 響應式布局
- 兼容性
- 移動端適配方案
- CSS應用
- CSS Modules(未完成)
- 分層
- 面向對象CSS(未完成)
- 布局
- 三列布局
- 單列等寬,其他多列自適應均勻
- 多列等高
- 圣杯布局
- 雙飛翼布局
- 瀑布流
- 1px問題
- 適配iPhoneX
- 橫屏適配
- 圖片模糊問題
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 內存空間
- 作用域
- 執行上下文棧
- 變量對象
- 作用域鏈
- this
- 類型轉換
- 閉包(未完成)
- 原型、面向對象
- class和extend
- 繼承
- new
- DOM
- Event Loop
- 垃圾回收機制
- 內存泄漏
- 數值存儲
- 連等賦值
- 基本類型
- 堆棧溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍歷對象屬性
- 寬度、高度
- performance
- 位運算
- tostring( ) 與 valueOf( )方法
- JavaScript技術
- 錯誤
- 異常處理
- 存儲
- Cookie與Session
- ES6(未完成)
- Babel轉碼
- let和const命令
- 變量的解構賦值
- 字符串的擴展
- 正則的擴展
- 數值的擴展
- 數組的擴展
- 函數的擴展
- 對象的擴展
- Symbol
- Set 和 Map 數據結構
- proxy
- Reflect
- module
- AJAX
- ES5
- 嚴格模式
- JSON
- 數組方法
- 對象方法
- 函數方法
- 服務端推送(未完成)
- JavaScript應用
- 復雜判斷
- 3D 全景圖
- 重載
- 上傳(未完成)
- 上傳方式
- 文件格式
- 渲染大量數據
- 圖片裁剪
- 斐波那契數列
- 編碼
- 數組去重
- 淺拷貝、深拷貝
- instanceof
- 模擬 new
- 防抖
- 節流
- 數組扁平化
- sleep函數
- 模擬bind
- 柯里化
- 零碎知識點
- 第四部分 進階
- 計算機原理
- 數據結構(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 選擇排序
- 插入排序
- 快速排序
- 搜索算法
- 動態規劃
- 二叉樹
- 瀏覽器
- 瀏覽器結構
- 瀏覽器工作原理
- HTML解析
- CSS解析
- 渲染樹構建
- 布局(Layout)
- 渲染
- 瀏覽器輸入 URL 后發生了什么
- 跨域
- 緩存機制
- reflow(回流)和repaint(重繪)
- 渲染層合并
- 編譯(未完成)
- Babel
- 設計模式(未完成)
- 函數式編程(未完成)
- 正則表達式(未完成)
- 性能
- 性能分析
- 性能指標
- 首屏加載
- 優化
- 瀏覽器層面
- HTTP層面
- 代碼層面
- 構建層面
- 移動端首屏優化
- 服務器層面
- bigpipe
- 構建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack優化
- Webpack原理
- 實現loader
- 實現plugin
- tapable
- Webpack打包后代碼
- rollup.js
- parcel
- 模塊化
- ESM
- 安全
- XSS
- CSRF
- 點擊劫持
- 中間人攻擊
- 密碼存儲
- 測試(未完成)
- 單元測試
- E2E測試
- 框架測試
- 樣式回歸測試
- 異步測試
- 自動化測試
- PWA
- PWA官網
- web app manifest
- service worker
- app install banners
- 調試PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 餓了么整理
- 樣式
- 技巧
- Vue音樂播放器
- Vue源碼
- Virtual Dom
- computed原理
- 數組綁定原理
- 雙向綁定
- nextTick
- keep-alive
- 導航守衛
- 組件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 動畫(未完成)
- 異常監控、收集(未完成)
- 數據采集
- Sentry
- 貝塞爾曲線
- 視頻
- 服務端渲染
- 服務端渲染的利與弊
- Vue SSR
- React SSR
- 客戶端
- 離線包
- 第五部分 網絡
- 五層協議
- TCP
- UDP
- HTTP
- 方法
- 首部
- 狀態碼
- 持久連接
- TLS
- content-type
- Redirect
- CSP
- 請求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服務端
- Linux
- Linux命令
- 權限
- XAMPP
- Node.js
- 安裝
- Node模塊化
- 設置環境變量
- Node的event loop
- 進程
- 全局對象
- 異步IO與事件驅動
- 文件系統
- Node錯誤處理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服務
- 負載均衡
- 獲取用戶IP
- 解決跨域
- 適配PC與移動環境
- 簡單的訪問限制
- 頁面內容修改
- 圖片處理
- 合并請求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自動化(未完成)
- docker
- 創建CLI
- 持續集成
- 持續交付
- 持續部署
- Jenkins
- 部署與發布
- 遠程登錄服務器
- 增強服務器安全等級
- 搭建 Nodejs 生產環境
- 配置 Nginx 實現反向代理
- 管理域名解析
- 配置 PM2 一鍵部署
- 發布上線
- 部署HTTPS
- Node 應用
- 爬蟲(未完成)
- 例子
- 反爬蟲
- 中間件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源碼
- app.js
- config.js
- 消息隊列
- RPC
- 性能優化
- 第七部分 總結
- Web服務器
- 目錄結構
- 依賴
- 功能
- 代碼片段
- 整理
- 知識清單、博客
- 項目、組件、庫
- Node代碼
- 面試必考
- 91算法
- 第八部分 工作代碼總結
- 樣式代碼
- 框架代碼
- 組件代碼
- 功能代碼
- 通用代碼