CSS의 cursor 속성을 이용하는 경우 구현의 한계가 있다.
요소를 생성하여 더 멋지게 커스텀 해본 예제이다.
JS로 체크해서 PC웹에서만 적용이 가능하다.
/* Smart Cursor Style */
.smart_cursor {display:none;position:fixed;left:0;top:0;z-index:10000;pointer-events:none;}
.smart_cursor.enter {display:block;}
.smart_cursor:before {content:'';display:block;width:20px;height:20px;border-radius:50%;background-color:#d33;transform:translate(-50%, -50%);} /* 디폴트 커서 */
.smart_cursor [data-smart-cursor] {opacity:0;visibility:hidden;width:100px;height:100px;position:absolute;left:-50px;top:-50px;background-repeat:no-repeat;background-size:100% 100%;transform:scale(0.2);transition:all 0.2s;}
.smart_cursor [data-smart-cursor].on {opacity:1;visibility:visible;transform:scale(1);}
.smart_cursor [data-smart-cursor='anchor'] {background-image:url('cursor_1.png');}
.smart_cursor [data-smart-cursor='drag'] {background-image:url('cursor_2.png');}
const SmartCursor = {
cursor: {},
enter: function(){
SmartCursor.cursor.parent.classList.add('enter');
},
leave: function(){
SmartCursor.cursor.parent.classList.remove('enter');
SmartCursor.cursor.parent.removeAttribute('style');
},
move: function(mouse){
if(!SmartCursor.cursor.parent.classList.contains('enter')){
SmartCursor.enter();
}
SmartCursor.cursor.parent.style.transform = 'translate(' + mouse.clientX + 'px, ' + mouse.clientY + 'px)';
},
toggle: function(cursorType){
SmartCursor.cursor[cursorType].classList.toggle('on');
},
init: function(){
// 커서 요소 생성
SmartCursor.cursor.parent = document.createElement('div');
SmartCursor.cursor.parent.classList.add('smart_cursor');
document.body.append(SmartCursor.cursor.parent);
const cursors = document.querySelectorAll('[data-cursor]');
if(cursors.length){
const tempCursors = []; // 페이지 내 존재하는 cursor를 담을 임시 배열
cursors.forEach(function(cursor, idx){
tempCursors.push(cursor.dataset.cursor);
cursor.addEventListener('mouseenter', function(){
SmartCursor.toggle(cursor.dataset.cursor);
});
cursor.addEventListener('mouseleave', function(){
SmartCursor.toggle(cursor.dataset.cursor);
});
});
// 임시 배열에서 중복되는 항목 제거
const realCursors = tempCursors.filter(function(item, idx){
return tempCursors.indexOf(item) === idx;
});
// 커서 요소 삽입
realCursors.forEach(function(ele, idx){
SmartCursor.cursor[ele] = document.createElement('div');
SmartCursor.cursor[ele].dataset.smartCursor = ele;
SmartCursor.cursor.parent.append(SmartCursor.cursor[ele]);
});
}
document.documentElement.addEventListener('mouseenter', SmartCursor.enter);
document.documentElement.addEventListener('mouseleave', SmartCursor.leave);
document.documentElement.addEventListener('mousemove', SmartCursor.move);
// iframe
const iframeObj = document.querySelectorAll('iframe');
if(iframeObj.length){
const frameEnter = function(){
document.querySelector('.smart_cursor').style.display = 'none';
};
const frameLeave = function(){
document.querySelector('.smart_cursor').style.display = '';
};
iframeObj.forEach(function(ele){
ele.addEventListener('mouseenter', frameEnter);
ele.addEventListener('mouseleave', frameLeave);
ele.addEventListener('mousemove', frameEnter);
});
}
// smart cursor를 적용하지 않는 요소 중 클릭 가능한 요소들
const clickable = document.querySelectorAll('a:not([data-cursor]), button:not([data-cursor]), label:not([data-cursor])');
if(clickable.length){
const clickableEnter = function(){
document.querySelector('.smart_cursor').classList.add('clickable');
};
const clickableLeave = function(){
document.querySelector('.smart_cursor').classList.remove('clickable');
};
clickable.forEach(function(ele){
ele.addEventListener('mouseenter', clickableEnter);
ele.addEventListener('mouseleave', clickableLeave);
});
}
}
};
SmartCursor.init();