<div class="l-wrapper">
<a href="" class="c-button">
リンクボタン
</a>
<a href="" class="c-button">
リンクボタン
</a>
</div>
.js-cursor {
}
.js-cursor__main {
pointer-events: none;
position: fixed;
top: 0;
left: 0;
width: 10px;
height: 10px;
background: #000;
border-radius: 50%;
z-index: 1001;
opacity: 0;
transition: opacity .2s;
.is-moved & {
opacity: 1;
}
.is-outside & {
opacity: 0;
}
}
.js-cursor__option {
pointer-events: none;
position: fixed;
top: 0;
left: 0;
width: 40px;
height: 40px;
border-radius: 50%;
background: #ddd;
z-index: 1000;
opacity: 0;
transition: opacity .2s .2s, background .2s;
.is-moved & {
opacity: 0.5;
}
.is-outside & {
opacity: 0;
}
.is-hover & {
background: #00FFFF;
}
}
.l-wrapper {
max-width: 700px;
width: 100%;
height: 1200px;
margin: 0 auto;
padding-top: 60px;
}
.c-button {
display: flex;
justify-content: center;
align-items: center;
width: 300px;
height: 50px;
background: #999;
margin-bottom: 40px;
color: #fff;
font-weight: bold;
&:hover {
background: #666;
}
}
View Compiled
class Cursor {
constructor() {
const el = `
<div class="js-cursor">
<div class="js-cursor__main"></div>
<div class="js-cursor__option"></div>
</div>`
document.body.insertAdjacentHTML('beforeend', el);
this.wrap_el = document.querySelector('.js-cursor');
this.main_el = document.querySelector('.js-cursor__main');
this.option_el = document.querySelector('.js-cursor__option');
this.position = {
mouseX: 0,
mouseY: 0,
currentX: 0,
currentY: 0
}
this.eventStatus = {
click: false,
hover: false
}
}
init() {
this.attachEvent();
this.tween();
}
attachEvent() {
//カーソルの位置を取得
document.addEventListener('mousemove', (e) => {
this.position.mouseX = e.clientX;
this.position.mouseY = e.clientY;
this.wrap_el.classList.add('is-moved');
});
//画面外判定
document.body.addEventListener("mouseleave", () => {
this.wrap_el.classList.add('is-outside');
}, false);
document.body.addEventListener("mouseenter", () => {
this.wrap_el.classList.remove('is-outside');
}, false);
//クリック判定
document.addEventListener('mousedown', (e) => {
this.eventStatus.click = true;
})
document.addEventListener('mouseup', (e) => {
this.eventStatus.click = false;
})
// aタグホバー判定
// 監視ターゲットの取得
const body = document.body;
// オブザーバーの作成
const observer = new MutationObserver(records => {
let link = document.querySelectorAll('a');
for (const target of link) {
target.addEventListener('mouseenter', (e) => {
this.eventStatus.hover = true;
this.wrap_el.classList.add('is-hover');
})
target.addEventListener('mouseleave', (e) => {
this.eventStatus.hover = false;
this.wrap_el.classList.remove('is-hover');
})
}
// 変化が発生したときの処理を記述
})
// 監視の開始
observer.observe(body, {
childList: true
})
}
tween() {
TweenMax.to({}, .001, {
repeat: -1,
onRepeat: () => {
//減速処理
this.position.currentX += (this.position.mouseX - this.position.currentX) * 0.5;
this.position.currentY += (this.position.mouseY - this.position.currentY) * 0.5;
TweenMax.set(this.main_el,
{
css: {
x: this.position.currentX - 5,
y: this.position.currentY - 5
}
});
TweenMax.to(this.option_el, 0.3,
{
css: {
x: this.position.currentX - 20,
y: this.position.currentY - 20,
scale: this.scale(this.eventStatus)
}
});
}
});
}
scale(v) {
if (v.hover == true && v.click == false) {
return 1.6
} else if (v.hover == false && v.click == true) {
return 0.6
} else if (v.hover == true && v.click == true) {
return 0.6
} else {
return 1
}
}
}
let cursor = new Cursor();
cursor.init();
View Compiled
This Pen doesn't use any external CSS resources.