※ 주의! 크로스 도메인(CORS)에 걸리지 않는 경우만 가능합니다...!!
※ 아직 해결하지 못한 사항
- iframe 범위를 벗어났을 때 되돌아오게 하는 처리 - 확대/축소하면서 범위를 벗어나는 경우 원래대로 돌아오는 위치가 기대하는 것처럼 되지 않는다...
- 크로스 도메인 - iframe 내부 요소의 너비, 높이를 알아내서 내부 요소를 iframe 크기에 맞게 scale을 조절시키는 게 이 확대/축소 구현의 관건인데 크로스 도메인 정책 때문에 현재 도메인과 iframe 내부에 호출하는 도메인이 다를 경우 처리가 불가능하다…!
■ 기능 구현까지의 서사
1) iframe 확대/축소 기능의 필요성
하이브리드 모바일 앱을 구축하면서 공지사항을 구현하는데 상세 내용으로 웹 페이지를 보여줘야 해서 iframe을 이용하게 되었다.
상세 내용을 스크롤 없이 iframe 크기에 맞춰 모두 보여주기 위해서
iframe 가로 사이즈 안에 상세 내용이 모두 보이게끔 iframe 내부 요소를 축소하고, 이 iframe 내부 요소의 높이에 맞게 iframe의 높이를 조절했는데 상세 내용이 웹을 기준으로 작성되어 있다보니 표나 사진이 있을 경우 모바일에서 보기엔 글자 크기가 너무 작아서 확대/축소 기능이 필수적이었다.
2) iframe 내부 확대/축소 기능 - 방법을 찾지 못함
iframe의 내부 요소 자체를 확대/축소하는 방법은 아직 찾지 못했다..
그래서 iframe 내부 요소를 iframe 크기에 딱 맞춰서 내부 html에 scale을 적용하고
iframe 자체를 확대/축소하면 되지 않을까 하고 방법을 찾아봤다.
3) iframe 자체를 확대/축소 - Panzoom과의 만남.. 그리고 문제점
iframe 확대/축소를 구글링하니 scale을 이용해서 정적으로 확대/축소하는 방법만 주구장창 나왔는데
그러다가 한줄기 빛과 같은 Panzoom을 만났다.
Panzoom 라이브러리는 scale등과 같은 css요소를 이용해서 동적으로 확대/축소를 할 수 있도록 도와준다고 한다.
Panzoom 깃허브 설명 중에 아래의 글이 있는데...
Panzoom uses CSS transforms to take advantage of hardware/GPU acceleration in the browser, which means the element can be anything: an image, a video, an iframe, a canvas, text, WHATEVER.
문제는... iframe일때 확대/축소가 안된다...!!!!!!!!!!!!!
4) 문제 해결 - part1
그래서 원인이 뭘까 생각하다가
iframe의 스크롤 이벤트랑 Panzoom 라이브러리의 핀치 이벤트가 서로 충돌해서 Panzoom의 핀치 줌 기능이 iframe에서는 제대로 동작하지 않는 거라는 생각이 들어서
아예 iframe의 스크롤 기능이 작동하지 않게 iframe 위를 덮어버리면 되지 않을까해서 아래와 같이 구현했다.
<div id="wrap">
<div id="layer"></div>
<iframe src="링크"/>
</div>
이렇게 하고 #wrap에 panzoom 기능을 적용하니까 성공적으로 iframe 확대 축소가 가능했다!!
예~~~ 드디어 성공!!! 하고 기뻐하려는 찰나....
5) 문제 해결 - part2 (최종)
상세 웹페이지에서 링크 이동을 하는 경우가 있다는 것을 알게 되었다...
그리고 중요한 건 iframe 내부의 링크를 클릭했을 때 iframe 내부에서 이동하지 않고 특정 함수를 호출해서 외부 브라우저(예: 삼성 인터넷)에서 해당 페이지가 열리게끔 처리해야 한다는 것이었다.
이 말은 즉, 아이프레임 내부의 href 요소를 지우고 해당 태그에 내가 원하는 함수를 click 이벤트에 걸어야 한다는 것!
오랫동안 이 기능을 어떻게 구현하나 걱정하다가 오늘 딱!!! 구글링하다가 해결방안을 찾았다.
https://stackoverflow.com/questions/15248970/iframe-prevents-iscroll-scrolling-on-mobile-safari
구세주님 좌표:
구현 방법은
① iframe 위에 레이어 div를 배치하고
② 해당 iframe 요소를 탭/클릭하려는 경우 레이어 div에서 감지한 x, y 좌표를 저장하고
③ iframe 콘텐츠 내의 해당 좌표에 대해 클릭 이벤트를 발생시키는 방식!
결과물은 아래에~~
아직 해결해야 하는 것들이 많이 있지만... 이렇게라도 구현한 것에... 나름 만족....!
■ 문제 해결 소스
● HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="https://unpkg.com/@panzoom/panzoom@4.4.4/dist/panzoom.min.js"></script>
<style>
#layer {
position: absolute;
opacity: 0;
z-index: 2;
}
</style>
</head>
<body>
<div id="panzoom_parent">
<div id="panzoom">
<div id="layer" class="full-size"></div>
<iframe src="해당링크" onload="iframeCssChange();" style="visibility: hidden; overflow: hidden;"></iframe>
</div>
</div>
</body>
</html>
● Javascript
// 확대/축소 설정
Panzoom(document.querySelector('#panzoom'), {
minScale: 1,
enableTextSelection : true,
canvas: true, // panzoom 기능 적용한 범위 이외의 다른 영역을 드래그해서도 이동시킬 수 있다.
});
// 팬/줌 시에 화면에서 iframe이 벗어나면 원상복귀
document.querySelector('#panzoom').addEventListener('panzoomend', (event) => {
var parentTop = $('#panzoom_parent').offset().top;
var parentBottom = offsetBottom('#panzoom_parent');
var parentLeft = $('#panzoom_parent').offset().left;
var parentRight = offsetRight('#panzoom_parent');
var childTop = $('#panzoom').offset().top;
var childBottom = offsetBottom('#panzoom');
var childLeft = $('#panzoom').offset().left;
var childRight = offsetRight('#panzoom');
var x = 0;
var y = 0;
var scale = panzoom.getScale();
if (parentLeft < childLeft && parentRight < childRight) {
x = (parentLeft - childLeft)/scale;
} else if (parentLeft > childLeft && parentRight > childRight) {
x = (parentRight - childRight)/scale;
}
if (parentTop < childTop && parentBottom < childBottom) {
y = (parentTop - childTop)/scale;
} else if (parentTop > childTop && parentBottom > childBottom) {
y = (parentBottom - childBottom)/scale;
}
if (x || y) {
panzoom.pan(x, y, {animate: true, relative: true});
}
});
var offsetBottom = function(el, i) {
i = i || 0;
return $(el)[i].getBoundingClientRect().bottom;
};
var offsetRight = function(el, i) {
i = i || 0;
return $(el)[i].getBoundingClientRect().right;
};
// iframe css 변경
var iframeCssChange = function() {
if ($('iframe').contents().find('html')) {
var iframeInitHeight = $('iframe').height();
var innerHtml = $('iframe').contents().find('html')[0];
var innerWidth = innerHtml.scrollWidth;
var innerHeight = innerHtml.scrollHeight;
var scale = +(Math.floor(($('iframe').width() / innerWidth) + "e+2") + "e-2"); // 소수점 둘째 자리까지
$('iframe').contents().find('html').css({'transform': 'scale(' + scale + ')', 'transform-origin' : '0% 0%', 'width' : innerWidth, 'height' : '0'});
// iframe 내부 영역의 크기와 iframe 크기를 비교해서 내부의 크기를 iframe 크기에 딱 맞게 정적으로 확대/
if (innerHeight * scale > iframeInitHeight) {
$('iframe').height(innerHeight * scale);
$('#layer').height(innerHeight * scale);
}
// iframe 내부 링크 처리
$('iframe').contents().find('[href]').each(function(){
var href = $(this).attr('href');
$(this).attr({
'href': 'javascript:void(0);',
'data-href': href
});
}).on("click",function(){ // 클릭 시 실행
var href = $(this).data("href");
// ~~~ href를 외부 브라우저에서 여는 함수 ~~~
});
// iframe 내부로 클릭 이벤트 전달
$(document).on("click", "#layer", function(event){
var iframe = $('iframe').get(0);
var iframeDoc = (iframe.contentDocument) ? iframe.contentDocument : iframe.contentWindow.document;
// Find click position (coordinates)
var x = event.offsetX;
var y = event.offsetY;
// Trigger click inside iframe
var link = iframeDoc.elementFromPoint(x, y);
var newEvent = iframeDoc.createEvent('HTMLEvents');
newEvent.initEvent('click', true, true);
link.dispatchEvent(newEvent);
});
}
$('iframe').css('visibility', '');
};
'프로그래밍 언어 > JavaScript' 카테고리의 다른 글
[JavaScript/Ajax] Ajax 배열(array) 전송 (Controller에 배열 보내기) (1) | 2022.07.08 |
---|---|
[JQuery] check box 관련 함수 (+배열 값 가져오기) (0) | 2022.07.07 |