前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SAO-UI-PLAN-Controlldot

SAO-UI-PLAN-Controlldot

作者头像
Akilar
发布2022-03-31 14:06:21
8830
发布2022-03-31 14:06:21
举报
文章被收录于专栏:Akilarの糖果屋

更新记录

2022-03-22:测试版

  1. 基本UI实现
  2. 基本功能实现

参考方向

教程原贴

动作监测参考

dorakika-导航测试

写在最前

虽然当时构思的时候想法很丰满,但是做出来以后突然觉得功能好鸡肋。左右浮动切换上下篇算是唯一的亮点了。上下按钮用拖动方式体感上还不如直接用侧栏菜单的按钮功能来的方便。尤其是考虑到PC端,手机端,窄屏设备,触屏电脑等设备动作的监测判断。总是牵扯到一大堆的交集。然后就是点击动作在某个屏宽比下会执行两次。 Dorakika的代码我也没吃透,似乎有个长按以后能够拖动悬浮菜单的功能,搞不好我多删了一些代码,长按以后拖动的结束动作一直没法按照期望的来。 这个悬浮按钮不打算实装了,作为学习用吧。bug太多,不修啦。

魔改步骤

SAO UI PLAN 相关项目为本站原创项目,因此均为内测版,在样式适配上仅针对本站进行调整,因此在泛用性上存在缺漏。对于可能遇到的 bug,欢迎在评论区进行讨论。

在进行本帖的魔改前,请务必做好备份以便回退。

新建 [Blogroot]\themes\butterfly\source\js\SAO-controlldot.js,

代码语言:javascript
复制
let touchStartTime,
	touchStartPos;				//start信息
let mouseDown = false;			//mousemove事件绑定在window上,mouseDown变量判断当前是否为悬浮菜单被按下,再进行move判断
let isMoveDot = false;			//悬浮菜单是否为可移动状态
let isFirstMove = false;		//因为move事件要移动才能触发,start时刷新为true,保证对第一次move的识别
let startTip;					//提示可移动

function start(e){
	isMoveDot = false;
	isFirstMove = true;
	touchStartTime = e.timeStamp;
	touchStartPos = [e.changedTouches[0].clientX,e.changedTouches[0].clientY];
	startTip = setTimeout(function(){
		// kk.changeMessage("现在可移动了",2000,'green');
	},1000);	//长按设置的是1000ms后可移动(不移动move不会触发,这里可以提示一下,进入move后可取消这个定时器)
	return false;
}

function move(e){
	let touchTime = e.timeStamp-touchStartTime;
	let currentPos = [e.changedTouches[0].clientX,e.changedTouches[0].clientY];
	let offset = [currentPos[0]-touchStartPos[0],currentPos[1]-touchStartPos[1]];
	clearTimeout(startTip);
	if(touchTime > 1000 && isFirstMove){
		isFirstMove = false;
		isMoveDot = true;
	}else if(isFirstMove && offset[1] == 0 && offset[0] == 0){
		// console.log("not move,just auto emit");
		return false;
	}
	isFirstMove = false;

	//移动
	if(isMoveDot){
		document.getElementById('SAO-ctrldot').style.top = `calc(${currentPos[1]*100/document.querySelector('html').clientHeight}% - 30px)`;
		document.getElementById('SAO-ctrldot').style.left = `calc(${currentPos[0]*100/document.querySelector('html').clientHeight}% - 30px)`;
		return false;
	}

	if(offset[0]**2 + offset[1]**2 > 30**2){
		const l = Math.sqrt(offset[0]**2+offset[1]**2);
		offset[0] = 30*offset[0]/l;
		offset[1] = 30*offset[1]/l;
	}

	document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].style.transform = `translate(${offset[0]}px,${offset[1]}px)`;
}


function end(e){
	if(isMoveDot){
		isMoveDot = false;
		return false;
	}
	let touchEndTime = e.timeStamp;
	let touchEndPos = [e.changedTouches[0].clientX,e.changedTouches[0].clientY];

	let offset = [touchEndPos[0]-touchStartPos[0],touchEndPos[1]-touchStartPos[1]];
	if(offset[0]**2 + offset[1]**2 > 30**2){
		const l = Math.sqrt(offset[0]**2+offset[1]**2);
		offset[0] = 30*offset[0]/l;
		offset[1] = 30*offset[1]/l;
	}
  // 点按时间,用于判断长按
	let touchTime = touchEndTime-touchStartTime;
	//判定事件
	if(touchTime < 300 && offset[0] == 0 && offset[1] == 0){
    // 监测到点按事件
		console.log("click");
    if ((/Android|webOS|BlackBerry/i.test(navigator.userAgent)) || (document.body.offsetWidth < 900)) { //媒体选择,为移动设备或者屏幕分辨率横屏小于900px
      //提取自main.js部分控制手机端设备目录显隐的代码片段。高耦合。
      if (window.getComputedStyle(document.getElementById('card-toc')).getPropertyValue('opacity') === '0') window.mobileToc.open()
      else window.mobileToc.close()
    }else{ //媒体选择,屏宽分辨率大于900px
      //提取自main.js部分控制侧栏折叠的代码片段。高耦合。
      const $htmlDom = document.documentElement.classList
      $htmlDom.contains('hide-aside')
        ? saveToLocal.set('aside-status', 'show', 2)
        : saveToLocal.set('aside-status', 'hide', 2)
      $htmlDom.toggle('hide-aside')
    }
	}else if(offset[0] == 0 && offset[1] == 0){
    // 监测到长按
		console.log("longClick");
    SAONotify("message","SAO Controller,正在制作中,敬请期待")

	}else if((offset[0]**2+offset[1]**2) < 400){
		console.log("little=>cancel");
	}else if(offset[0] > 0 && (Math.atan(offset[1]/offset[0])/Math.PI*180 < 45) && (Math.atan(offset[1]/offset[0])/Math.PI*180 > -45)){
    //监测到向右
		// console.log("right")
    var nextPost = document.querySelector(".next-post a")
    if(nextPost){//若存在下一篇
      nextHref = nextPost.getAttribute("href");
      nextTitle = nextPost.getAttribute("title");
      nextMessage = "是否跳转至下一篇:<br>" + nextTitle;
      nextAction = `pjax.loadUrl('`+nextHref+`')`;
      SAONotify("Message",nextMessage,nextAction);
    }else{
      SAONotify("Alert","已经没有下一篇了哦");
    }


	}else if(offset[0] < 0 && (Math.atan(offset[1]/-offset[0])/Math.PI*180 < 45)	&& (Math.atan(offset[1]/-offset[0])/Math.PI*180 > -45)){
    //监测到向左
		// console.log("left")
    var prevPost = document.querySelector(".prev-post a")
    if(prevPost){//若存在下一篇
      prevHref = prevPost.getAttribute("href");
      prevTitle = prevPost.getAttribute("title");
      prevMessage = "是否跳转至上一篇:<br>" + prevTitle;
      prevAction = `pjax.loadUrl('`+prevHref+`')`;
      SAONotify("Message",prevMessage,prevAction);
    }else{
      SAONotify("Alert","已经没有上一篇了哦");
    }


	}else if(offset[1]<0){
    // 监测到向上
		// console.log("up");
    btf.scrollToDest(0, 500); //返回顶部
	}else if(offset[1]>0){
    // 检测到向下
		// console.log("down");
    // 跳转到评论区
    let h;
	if(document.getElementById("post-comment")){
		h = document.getElementById('post-comment').offsetTop;
	}else if(document.getElementById('footer')){
		h = document.getElementById('footer').offsetTop;
	}
	// console.log(h)
	btf.scrollToDest(h, 500);
    // FixedCommentBtn(); //展开评论区
	}else{
    // 未知事件
		// console.log("???")
    SAONotify("Alert","无效的操作"); //弹窗提示无效操作
	}
    // 按钮复位
	document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].style.transform = `translate(0px,0px)`;

	return false
}
// 手机端触摸开始事件监听
document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].addEventListener("touchstart", start);
// PC端触摸开始事件监听
document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].addEventListener('mousedown',function(e){
  // if ((/Android|webOS|BlackBerry/i.test(navigator.userAgent)) || (document.body.offsetWidth < 900)) return true
	mouseDown=true;
  // console.log("mStart")
	e.changedTouches = [{clientX:e.clientX,clientY:e.clientY}]
	start(e)
	return false;
});
// 手机端触摸事件监听
document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].addEventListener('touchmove',move);
// PC端触摸事件监听
window.addEventListener('mousemove',function(e){
  // if ((/Android|webOS|BlackBerry/i.test(navigator.userAgent)) || (document.body.offsetWidth < 900)) return true
	if(mouseDown){
		e.changedTouches = [{clientX:e.clientX,clientY:e.clientY}];
		move(e);
		return false;
	}
});
//手机端触摸结束事件监听
document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].addEventListener('touchend',end);
// PC端触摸结束事件监听
window.addEventListener('mouseup',function(e){
  if ((/Android|webOS|BlackBerry/i.test(navigator.userAgent)) || (document.body.offsetWidth < 900)) return true
	if(mouseDown){
		mouseDown=false;
		e.changedTouches = [{clientX:e.clientX,clientY:e.clientY}]
		end(e,this)
		return false;
	}
});
//手机端触摸取消事件监听(用于复位)
document.getElementById('SAO-ctrldot').addEventListener('touchcancel',function(e){
	console.log("cancel",e)
	document.getElementById('SAO-ctrldot').getElementsByClassName('SAO-ctrldot-dot')[0].style.transform = `translate(0px,0px)`;
})

新建 [Blogroot]\themes\butterfly\layout\includes\custom\SAO-controlldot.pug

代码语言:javascript
复制
//- 悬停菜单主体
#SAO-ctrldot
  .SAO-ctrldot-bg
  .SAO-ctrldot-dot

新建[Blogroot]\themes\butterfly\source\css\_layout\SAO-controlldot.styl

代码语言:javascript
复制
@media screen and (max-width: 900px)
  #SAO-ctrldot
    right calc(50% - 30px) !important
    top unset !important
    bottom 40px
#SAO-ctrldot
  display flex
  position fixed
  right 60px
  top calc( 50% - 30px )
  width 60px
  height 60px
  justify-content center
  align-items center
  z-index 1000
  .SAO-ctrldot-bg
    position absolute
    background-color rgba(0, 0, 0, 0.6)
    width 60px
    height 60px
    border-radius 50%
  .SAO-ctrldot-dot
    position absolute
    width 40px
    height 40px
    border-radius 50%
    background-color rgba(255, 255, 255, 0.6)
    transition 0.1s

修改[Blogroot]\themes\butterfly\layout\includes\layout.pug, 在最后一行加入:

代码语言:javascript
复制
          footer#footer(style=footer_bg)
            !=partial('includes/footer', {}, {cache:theme.fragment_cache})
      else
        include ./404.pug
      include ./rightside.pug
      !=partial('includes/third-party/search/index', {}, {cache:theme.fragment_cache})
      include ./additional-js.pug
+     include ./custom/SAO-controller.pug

[Blogroot]\_config.butterfly.ymlinject配置项引入 js:

代码语言:javascript
复制
inject:
  head:
    #SAO-Notify弹窗的依赖。
    - <script src="https://npm.elemecdn.com/akilar-candyassets/js/SAO-Notify.js" async></script>
  bottom:
    - <script src="/js/SAO-controller.js" defer></script>

改进方向

可以考虑仅作为手机端的功能,直接用 @media 让它在 PC 端隐藏,js 里也用媒体选择给它屏蔽掉。这样子的话能避免屏宽比和设备的影响。 还有就是手机端按钮存在遮挡正文的问题,貌似 Dorakika 是有设计可以拖动位置的,但是代码大概给我误删了。 总的来说,这个悬浮按钮功能会给人眼前一亮的感受,但是因为上下左右点按长按总共不过六个动作,其实能够装载的功能也就那么多。单纯六个动作的话,侧栏按钮就能处理好。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 写在最前
  • 魔改步骤
  • 改进方向
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档