Angular实现悬浮球组件
程序员文章站
2022-05-29 18:14:25
...
Angular实现悬浮球组件
在手机 App 上,我们经常会看到悬浮球的东东,用着可能很舒服,但是 web 网页上却很少见,今天我们就通过 Angular 来实现,当然使用其他框架也是可以的。
功能要求:
- 支持设置直径
- 支持点击触发信号
- 支持设置鼠标按压时间
实现的过程中省略的部分天填坑过程。
此项目已经在github开源,欢迎大家 star 和 fork。不胜感激。
开发环境
Angular CLI: 6.1.2
Node: 8.9.4
OS: linux mint 19 x64
IDE: vscode
确定悬浮球的样式
我大致找了几个比较漂亮一点的悬浮球作为参考:
1. 魅族手机悬浮球
2. 小米手机的悬浮球
3. 不知名
看了这几个悬浮球,实在感觉一般般,接下来我们来试一试吧。
创建悬浮球组件
-
打开终端执行
ng g c floatingBall
此命令生成
floating-ball
组件,这里要感谢 Angular 脚手架的强大。floating-ball/ ├── floating-ball.component.html ├── floating-ball.component.scss ├── floating-ball.component.spec.ts └── floating-ball.component.ts 0 directories, 4 files
修改css文件为scss,相应在ts文件中的Component装饰器中也要修改。
-
html 文件增加如下
<div id="floating-ball-container" (click)="clicked.emit();" [style.cursor]="currentCursorStyle" [style.width]="addUnit(outerCircleDiameter)" [style.height]="addUnit(outerCircleDiameter)"> <div id="inner-circle" [style.width]="addUnit(innerCircleDiameter)" [style.height]="addUnit(innerCircleDiameter)" [style.top]="addUnit(outerCircleDiameter / 2 - innerCircleDiameter / 2)" [style.left]="addUnit(outerCircleDiameter / 2 - innerCircleDiameter / 2)"></div> </div>
html
主要是两个块级元素div
,想了解块级和内联元素的区别点击这里。第一个
div
为悬浮球的容器,第二个div
为悬浮球的内圆。 -
scss文件修改如下
$inner-circle-bg: white; // 内圆的背景色 $container-bg-color: #F44336; // 悬浮球容器的背景色 $container-bg-start-color: #EF9A9A; // 悬浮球动画背景开始颜色 // 悬浮球scss配置 #floating-ball-container { position: fixed; z-index: 2000; // 设置为最大原因是保持再所有元素的上层 bottom: 20px; right: 20px; height: 60px; width: 60px; border: none; border-radius: 50%; opacity: 1; // 设置阴影效果 box-shadow: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.2); // 动画效果, 一闪一闪的效果 animation: twinkle 1.5s alternate infinite; -moz-animation:twinkle 1.5s alternate infinite; /* Firefox */ -webkit-animation:twinkle 1.5s alternate infinite; /* Safari and Chrome */ -o-animation:twinkle 1.5s alternate infinite; /* Opera */ // 容器内的属性 #inner-circle { position: relative; width: 30px; height: 30px; top: 15px; left: 15px; border-radius: 50%; background-color: $inner-circle-bg; opacity: 1; } } @keyframes twinkle{ from{background: $container-bg-start-color;} to{background: $container-bg-color;} } @-moz-keyframes twinkle{ /* Firefox */ from{background: $container-bg-start-color;} to{background: $container-bg-color;} } @-webkit-keyframes twinkle{ /* Safari and Chrome */ from{background: $container-bg-start-color;} to{background: $container-bg-color;} } @-o-keyframes twinkle{ /* Opera */ from{background: $container-bg-start-color;} to{background: $container-bg-color;} // 鼠标悬浮后的伪类样式设定 #floating-ball-container:hover { animation-play-state: paused; // 所有动画停止 // 阴影加深 24dp box-shadow: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.2); }
ts文件修改如下
import { Component, AfterViewInit,
EventEmitter, Output } from '@angular/core';
@Component({
selector: 'app-floating-ball',
templateUrl: './floating-ball.component.html',
styleUrls: ['./floating-ball.component.scss']
})
export class FloatingBallComponent implements AfterViewInit {
// 点击悬浮球信号
@Output() public clicked = new EventEmitter();
@Input() outerCircleDiameter = 60; // 外圆直径
@Input() innerCircleDiameter = 30; // 内圆直径
@Input() pressedTime = 500; // 鼠标按压时间
isPressed = false; // 鼠标是否按下的标记
posX = 0; // 悬浮球的x轴位置
posY = 0; // 悬浮球的y轴位置
lastMousePos = { // 记录鼠标按下时的坐标
x: 0,
y: 0
};
mouseOffsetX = 0; // 鼠标X偏移量
mouseOffsetY = 0; // 鼠标X偏移量
elementOffsetX = 0; // 悬浮球容器的X偏移量
elementOffsetY = 0; // 悬浮球容器的Y偏移量
private timer: any;
currentCursorStyle = 'default';
private cursorStyle = { default: 'default', moved: 'move' };
constructor() { }
ngAfterViewInit() {
const rootNode = document.getElementById('floating-ball-container'); // 获取容器元素
const viewWidth = window.innerWidth; // 获取窗口宽度
const viewHeight = window.innerHeight; // 获取窗口宽度
rootNode.addEventListener('mousedown', (event) => {
this.timer = setInterval(() => {
this.isPressed = true; // 确认鼠标按下
this.openMovedCursor(); // 打开可移动光标
}, this.pressedTime);
this.lastMousePos.x = event.clientX; // 记录鼠标当前的x坐标
this.lastMousePos.y = event.clientY; // 记录鼠标当前的y坐标
this.elementOffsetX = rootNode.offsetLeft; // 记录容器元素当时的左偏移量
this.elementOffsetY = rootNode.offsetTop; // 记录容器元素的上偏移量
event.preventDefault(); // 取消其他事件
}, false);
// 此处必须挂载在document上,否则会发生鼠标移动过快停止
document.addEventListener('mousemove', (event) => {
if (this.isPressed) {// 如果是鼠标按下则继续执行
this.mouseOffsetX = event.clientX - this.lastMousePos.x; // 记录在鼠标x轴移动的数据
this.mouseOffsetY = event.clientY - this.lastMousePos.y; // 记录在鼠标y轴移动的数据
this.posX = this.elementOffsetX + this.mouseOffsetX; // 容器在x轴的偏移量加上鼠标在x轴移动的距离
this.posY = this.elementOffsetY + this.mouseOffsetY; // 容器在y轴的偏移量加上鼠标在y轴移动的距离
rootNode.style.left = this.posX + 'px';
rootNode.style.top = this.posY + 'px';
}
}, false);
// 鼠标释放时候的函数
document.addEventListener('mouseup', () => {
this.isPressed = false;
this.closeMovedCursor();
clearInterval(this.timer); // 释放定时器
}, false);
rootNode.addEventListener('touchmove', (event) => {
event.preventDefault(); // 阻止其他事件
if (event.targetTouches.length === 1) {
const touch = event.targetTouches[0]; // 把元素放在手指所在的位置
this.posX = touch.pageX; // 存储x坐标
this.posY = touch.pageY; // 存储Y坐标
rootNode.style.left = this.posX + 'px';
rootNode.style.top = this.posY + 'px';
}
});
}
openMovedCursor(): void {
if (this.currentCursorStyle === this.cursorStyle.moved) {
return;
}
this.currentCursorStyle = this.cursorStyle.moved;
}
closeMovedCursor(): void {
if (this.currentCursorStyle === this.cursorStyle.default) {
return;
}
this.currentCursorStyle = this.cursorStyle.default;
}
addUnit(value: number): string {
return value + 'px';
}
}
-
在app中使用
<app-floating-ball></app-floating-ball>
-
运行查看
npm start
结果展示:
上一篇: 在HANA中寻求大数据突围
下一篇: QQ测拉效果实现(三)