ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、视频、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[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失焦后页面没有回弹 ![](https://img.kancloud.cn/0a/17/0a175f512a45f24fb3b21feeab367791_320x580.gif) 一般出现在`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') ~~~