반응형

이미지 뷰어형태로 당장 올릴 이미지를 미리보기형태로 작은모습으로 보여주는 기능을 만들고 있었는데, 스마트폰에서 찍은 사진을 테스트하다 보면 자동으로 회전되어 보이는 현상이 있었습니다.

 

감자♥ 정상적으로 나옵니다.

 

 

꼬미♥ 꼬미야 왜 돌아갔니...

 

업로드 하고 페이지에 노출하는 형태라면 서버단에서 저장할 때, 처리하면 되지만 업로드 뷰어의 경우에는 스크립트단에서 해결이 필요하였습니다.

 

 

스크립트 라이브러리 중 Load-Image의 힘을 빌려 해결하였습니다.

github.com/blueimp/JavaScript-Load-Image#image-loading

 

blueimp/JavaScript-Load-Image

Load images provided as File or Blob objects or via URL. Retrieve an optionally scaled, cropped or rotated HTML img or canvas element. Use methods to parse image metadata to extract IPTC and Exif t...

github.com

 

위 github에서 라이브러리를 받을 수 있습니다.

 

EXIF 

디지털 카메라와 스마트폰등에서 이미지 등의 정보를 기록할 수 있습니다.

해당 메타정보 중에 회전 방향 옵션을 받아올 수 있는데 이 회전 방향값인 orientation을 추출하고 처리하여 강제로 고정시키는 방식입니다.

단순하게 업로드 한 데이터에서는 회전정보를 알 수가 없다보니 blob을 통해 바이너리 데이터로 변환하여 데이터를 확인하는 절차를 따른다고 합니다.

 

Load-Image

html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"
    integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<script src="./load_image/load-image.all.min.js"></script>
<body>
    <input type="file" id="upload">
    <img src="" alt="" id="preview" style="width:300px;height:300px;">
</body>

</html>

 

javascript

$(function () {
    $("#upload").on("change", function (e) {
        var files = e.target.files;
        var fileType = files[0].type;
        var limg = loadImage(files[0], function (img, data) {
            img.toBlob(function (blob) {
                var rotateFile = new File([blob], files[0].name, { type: fileType });
                var reader = new FileReader();
                reader.onload = function (e) { $("#preview").attr("src", e.target.result); }

                reader.readAsDataURL(rotateFile);
            }, fileType)
        }, { orientation: 1 });
    });
})

file 태그에 이미지가 올라가면 change이벤트에 의해 파일 정보를 받아오고 라이브러리 함수 loadImage를 통해 데이터를 blob으로 읽고 image태그에 처리를 해줍니다.

 

여기서 포인트는 2번째 파라미터 object값에 orientation값을 1로 해주면 세로 이미지로 볼 수 있었습니다.

여기서 값을 2로 처리하시면 좌우 반전되어 나오는 것을 볼 수 있습니다.

 

 

처리 동작 후 미리보기 이미지가 정상적인 회전으로 나옵니다.

반응형
반응형

입력폼에 컨텐츠에 해당하는 내용을 적도록 할때 textarea 또는 에디터 툴을 적용하여 장문을 지원하는데, 이때 사용자가 엔터와 같은 개행문자를 입력하지 않고, 연속되게 이어서 입력하게되면 나중에 데이터를 받아와서 parsing시 그냥 한줄로 쭉 들어와서 정상적으로 노출되지 않는 현상이 존재한다.

 

이 때, CSS의 word-wrap옵션을 통해 처리가 가능하다.

 

 

word-wrap

word-wrap:break-word;

 

적용전 문제가 되는 모습

개행이 없다보니 그대로 넣게되면 깨지는 현상을 볼 수 있다.

 

word-wrap: break-word;적용

.contents p {word-wrap: break-word;}

적용 후에는 넓이값에 도달하면 알아서 줄바꿈이 일어난다.

 

normal 옵션도 존재하는데, 이 경우에는 기존처럼 문자열이 뚫고 나온다.

반응형
반응형

사실 단순한 pdf파일을 보이게만 하는건 몇몇 태그를 활용하여 쉽게 처리가 가능합니다.

 

iframe

<iframe src="/image/test.pdf" style="width:700px;height:700px;"></iframe>

IE를 제외하고 firefox, chrome, edge등 모두 정상적으로 뷰어 동작을 하는것을 볼 수 있습니다.

IE에서는 다운로드 이벤트가 발생합니다.

 

 

 

embed

<embed src="/image/test.pdf" type="application/pdf" width=700px height=700px/>

iframe과 비슷하지만 type과 넓이 높이를 따로 지정해줘야 합니다.

마찬가지로 IE를 제외하고 전부 정상 동작하는것을 볼 수 있습니다.

 

 

 

 

PDFObject

pdfobject.com/

 

PDFObject: A JavaScript utility for embedding PDFs

width [string]. Default: "100%" Will insert the width as an inline style via the style attribute on the element. If left unspecified, PDFObject will default to 100%. Is standard CSS, supports all units, including px, %, em, and rem. Tip: It's safer to spec

pdfobject.com

PDF를 읽어서 뷰어 역할을 해주는 훌륭한 js 라이브러리입니다.

해당 브라우저에서 PDF를 읽지못한다면 특정 메시지와 함께 다운로드를 제공하고 있습니다.

 

IE에서 동작이 안되는 브라우저는 친절하게 다운로드 링크를 제공한다.

 

사용법은 생각보다 간단합니다.

위의 URL을 통해 PDFObject를 다운로드 받은 후, PDFObject를 연결줍니다.

<div id="myPdf"></div>
<script type="text/javascript" src="/js/common/component/PDFObject/pdfobject.js"></script>
PDFObject.embed("/image/guide.pdf", "#myPdf");

첫번째 파라미터는 pdf파일 위치를 두번째 파라미터는 만들어줄 타겟 대상을 입력합니다.

 

 

full 버전으로 잘 읽힙니다.

 

세번째에는 옵션값을 넣을 수 있습니다.

var option = {
		height: "400px",
		pdfOpenParams: {view: 'FitV', page: '2'}
}
PDFObject.embed(RESOURCES_PATH + "/image/test.pdf", "#myPdf", option);

높이가 400으로 줄었습니다.

높이를 400으로 줄여보았습니다.

 

 

그럼 이쯤에서 IE에서는 pdf 뷰어를 못하나요? 궁금해하실텐데 IE11에서만 동작하긴 하지만 또 방법이 있습니다.

 

 

 

 

PDF.js + PDFObject.js

먼저 알아보았던 PDFObject에 Pdf.js를 활용하면 가능합니다.

뷰어처럼 동작하도록 뼈대의 프레임 html을 사용하고 각 태그별에 맞는 이벤트 동작을 제공합니다.

 

mozilla.github.io/pdf.js/

 

PDF.js

PDF.js A general-purpose, web standards-based platform for parsing and rendering PDFs. Download Demo GitHub Project

mozilla.github.io

해당 URL에서 PDF.js를 다운로드 하실 수 있습니다.

PDFObject와 같은 위치에 다운로드한 파일들을 옮겨줍니다.

 

이번에는 div태그에 사이즈를 입력해야합니다.

<div id="myPdf" style="width:550px;height:400px;"></div>
<script type="text/javascript" src="/js/common/component/PDFObject/pdfobject.js"></script>
var option = {
	pdfOpenParams: {
		navpanes: 0,
		toolbar: 0,
		statusbar: 0,
		view: "FitV",
		page: 1
	},
	width : "550px",
	height: "400px",
	forcePDFJS: true,
	PDFJS_URL: "/js/common/component/PdfJs/web/viewer.html"
}
PDFObject.embed("/image/test.pdf", "#myPdf", option);

 

 IE11기준에서만 동작하긴 하지만 아래와 같이 익스에서도 pdf뷰어 기능을 구현할 수 있습니다.

반응형
반응형

position값을 사용하여 처리하면 center같은 옵션이 먹지 않는데, 이런 경우에는 약간의 연산이나 transform옵션을 활용하면 중앙 정렬 또는 가운데 정렬등을 처리 할 수 있습니다.

transform을 통한 센터정렬, 중앙정렬하기

 

HTML - 예시 데이터

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Centered</title>
</head>
<style>
    * {
        margin: 0px;
        padding: 0px;
    }

    .popup {
        width: 300px;
        height: 450px;
        border: 1px solid gray;
        background-color: aquamarine;
        position: absolute;
        top: 0%;
        left: 0%;
    }
</style>

<body>
    <div class="popup"></div>
</body>

</html>

 

샘플

이러한 형태의 div를 중앙 정렬을 또는 가운데 정렬을 처리해보겠습니다.

 

 

1. top, left에 50%씩 처리하여 가운데로 밀어넣기

.popup 클래스에 top, left를 50퍼로 수정합니다.

.popup {
    width: 300px;
    height: 450px;
    border: 1px solid gray;
    background-color: aquamarine;
    position: absolute;
    top: 50%;
    left: 50%;
}

 

전체의 가운데 위치 기준으로 생성되었다.

중앙을 기준으로 가로 300px, 높이 450px짜리 div가 처리된것을 볼 수 있습니다.

이부분에서 transform을 추가하여 완벽한 중앙 정렬을 처리합니다.

 

2. transform으로 요소 사이즈만큼 반대로 이동시키기

.popup {
    width: 300px;
    height: 450px;
    border: 1px solid gray;
    background-color: aquamarine;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

중앙 정렬!

 

원리는 아래와 같습니다.

개체의 넓이와 높이만큼 50퍼센트를 반대로 이동합니다.

 

 

가운데 정렬

높이는 30px만큼 고정으로 띄우고 가운데만 정렬하고자 할때는 아래와 같이합니다.

.popup {
    width: 300px;
    height: 450px;
    border: 1px solid gray;
    background-color: aquamarine;
    position: absolute;
    top: 30px;
    left: 50%;
    transform: translate(-50%, 0%);
}

 

높이는 고정 시키고 가운데 정렬

 

 

translate의 첫번째 x축인 가로축만 50퍼센트만큼 당겨와서 가운데 정렬을 처리할 수 있습니다.

반응형
반응형

캔버스에 마우스 이벤트를 통해 클릭을 하는순간 그리기모드로 전환 후

마우스 이동에 따라 선을 그리고 마우스가 영역을 벗어나거나 마우스 클릭을 떼는 순간 그리기를 멈추는 소스를 작성해보겠습니다.

 

사인 만들기(그림 그리기)

See the Pen CanvasDrawSign by myhappyman (@myhappyman) on CodePen.

 

동작 모습

잘 그려진다!

반응형
반응형

jQuery-ui를 사용하여 드래그 이벤트와 리사이즈 이벤트를 추가 할 수 있는데, 직사각형의 영역을 서로 침범할 경우 기존 위치로 원위치 시키는 방법을 알아보겠습니다.

 

겹침 처리 방법

1. 일반 2차원 배열에 0으로 채워넣고, 도형이 있는 위치는 1로 채웁니다.

도형이 존재하는 x,y에 넓이 높이 만큼 영역을 1로 채웁니다.

 

2. 도형 위치마다 1로 더하기때문에 겹치는 영역이 생기면 아래처럼 2가 존재하게 됩니다.

겹치는 영역은 2가 된다.

 

3. 각 도형마다의 위치와 넓이, 높이를 기억하고 있다가 겹치면 원위치 시킨다.

 

 

실적용 소스

html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    <link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/themes/smoothness/jquery-ui.css">
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0
        }

        #box {
            width: 500px;
            height: 500px;
            border: 1px solid black;
            background: #eee;
        }

        #box .rect {
            position: absolute;
            width: 50px;
            height: 50px;
            border: 1px solid black;
        }
    </style>
</head>

<body>
    <div id="box">
        <div class="rect" idx=0></div>
        <div class="rect" idx=1></div>
    </div>
    <script src="./draggable_rect.js"></script>
</body>

</html>

 

draggable_rect.js

var rectArr = [];
var canvasArr = [];

$(function () {
    //초기 세팅
    for (i = 0; i < $(".rect").length; i++) {
        var rect = $($(".rect")[i])
        var idx = rect.attr("idx");
        var x = rect.offset().left;
        var y = rect.offset().top;

        var left = rect.css("left");
        var top = rect.css("top");
        var w = rect.width();
        var h = rect.height();
        rectArr[idx] = { x: x, y: y, left: left, top: top, w: w, h: h };
    }



    var width = $("#box").width();
    var height = $("#box").height();
    for (i = 0; i < height; i++) {
        canvasArr[i] = new Array(width);
    }

    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j++) {
            canvasArr[i][j] = 0;
        }
    }
});

$(".rect").draggable({
    containment: "parent",
    start: function (event, ui) {

    },
    stop: function (event, ui) {
        var idx = $(this).attr("idx");
        var left = $(this).css("left");
        var top = $(this).css("top");
        var x = $(this).offset().left;
        var y = $(this).offset().top;
        var w = $(this).width();
        var h = $(this).height();
        var thisObj = { x: x, y: y, w: w, h: h, idx: idx };
        if (overLapChecker(".rect", thisObj)) {
            //통과
            rectArr[idx] = { x: x, y: y, left: left, top: top, w: w, h: h };
        } else {
            //겹침 - 초기화
            var obj = rectArr[idx];
            $(".rect[idx=" + idx + "]").css("top", obj.top);
            $(".rect[idx=" + idx + "]").css("left", obj.left);
        }
    }
}).resizable({
    containment: "parent",
    start: function (event, ui) {

    },
    stop: function (event, ui) {
        var idx = $(this).attr("idx");
        var left = $(this).css("left");
        var top = $(this).css("top");
        var x = $(this).offset().left;
        var y = $(this).offset().top;
        var w = $(this).width();
        var h = $(this).height();
        var thisObj = { x: x, y: y, w: w, h: h, idx: idx };
        if (overLapChecker(".rect", thisObj)) {
            //통과
            rectArr[idx] = { x: x, y: y, left: left, top: top, w: w, h: h };
        } else {
            //겹침 - 초기화
            var obj = rectArr[idx];
            $(".rect[idx=" + idx + "]").css("top", obj.top);
            $(".rect[idx=" + idx + "]").css("left", obj.left);
            $(".rect[idx=" + idx + "]").css("width", obj.w);
            $(".rect[idx=" + idx + "]").css("height", obj.h);
        }
    }
})



function overLapChecker(className, thisObj) {
    var len = $(className).length;
    if (len > 0) {
        var thisMapArr = JSON.parse(JSON.stringify(canvasArr)); //빈 배열을 복사한다.
        var x = thisObj.x;
        var y = thisObj.y;
        var w = thisObj.w;
        var h = thisObj.h;
        for (i = y; i < y + h; i++) {
            for (j = x; j < x + w; j++) {
                thisMapArr[i][j] = thisMapArr[i][j] + 1;
            }
        }
        for (z = 0; z < len; z++) {
            var idx = $($(className)[z]).attr("idx");
            if (idx == thisObj.idx) {
                continue;
            } else {
                var checkerArr = JSON.parse(JSON.stringify(thisMapArr));
                var x2 = $(".rect[idx=" + idx + "]").offset().left;
                var y2 = $(".rect[idx=" + idx + "]").offset().top;
                var w2 = $(".rect[idx=" + idx + "]").width();
                var h2 = $(".rect[idx=" + idx + "]").height();

                for (i = y2; i < y2 + h2; i++) {
                    for (j = x2; j < x2 + w2; j++) {
                        checkerArr[i][j] = checkerArr[i][j] + 1;
                        if (checkerArr[i][j] > 1) {
                            return false;
                        }
                    }
                }
            }
        }

    }
    return true;
}

드래그, 리사이즈 이벤트 처리를 하고 각 도형의 위치를 특정 배열에 담아둡니다.

 

드래그 또는 리사이즈 이벤트가 멈추면 멈추는 동시에 위치를 체크합니다. 겹치는 동선이 존재하여 배열에(2)가 처리되면 원위치 시킵니다.

 

적용 모습

겹침이 방지되었다!

반응형
반응형

 

css를 활용하여 div를 동그라미로 만들었고 jQuery-ui의 Draggable 이벤트를 추가하였습니다.

 

기본적으로 position을 활용하기 때문에 서로 두개의 도형을 위 이미지형태로 처리하면 겹치게 되는데, 두 개 이상의 원형이 있을경우 겹치지 않도록 처리해보겠습니다.

 

소스를 보기전에 간단하게 겹침처리 방지에 사용한 방식을 설명하겠습니다.

위 그림의 공식을 사용하였습니다.

각 도형의 좌측, 상단의 좌표값을 알때 중간점을 구할수 있고, 중간점과 중간점의 거리를 구해서

그 거리값이 반지름 + 반지름한 값보다 작다면 겹쳤다고 판단하여 원래 위치로 돌리는 소스입니다.

 

 

드래그이벤트 원형 겹침 방지

See the Pen BaLQdbG by myhappyman (@myhappyman) on CodePen.

반응형
반응형

캔버스 내 클릭이벤트를 받아 처음에는 점만 그리고 한번 더 클릭하면 두 점을 잇는 선을 생성하고 두번째 생성된 위치에 화살표 모양을 처리해보겠습니다.

해당 기능을 구현하면서 Miscro Soft사의 파워포인트 기능들이... 엄청 훌륭하다는것을 다시한번 느꼈습니다!

수학적 기능이 많이 들어갔고, 두 점 사이의 좌표값을 알때, 사이각을 atan2를 통해 구하고 삼각형의 빗변을 구하는 공식을 통해 기준점을 정해 양 사이의 화살표처리를 하여 마무리하였습니다.

 

마지막에 화살표 처리를 위해 삼각형을 억지로 그려넣으면서 꽤나 뻘짓의 끝을 달렸는데, 수학적으로 접근하여 처리하니 어떤 방향에서든 정상적으로 처리되었습니다.

 

문제 해결에 도움되었던 사이트로는 아래 URL을 참조하였습니다.

참조 문서 : https://riptutorial.com/html5-canvas/example/18136/line-with-arrowheads

 

html5-canvas - Line with arrowheads | html5-canvas Tutorial

html5-canvas documentation: Line with arrowheads

riptutorial.com

 

동작 모습.gif

power point는 대단하구나...!

 

 

화살표가 생기는 Canvas

See the Pen CanvasToDrawArrow by myhappyman (@myhappyman) on CodePen.

반응형