HTML preprocessors can make writing HTML more powerful or convenient. For instance, Markdown is designed to be easier to write and read for text documents and you could write a loop in Pug.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. So you don't have access to higher-up elements like the <html>
tag. If you want to add classes there that can affect the whole document, this is the place to do it.
In CodePen, whatever you write in the HTML editor is what goes within the <body>
tags in a basic HTML5 template. If you need things in the <head>
of the document, put that code here.
The resource you are linking to is using the 'http' protocol, which may not work when the browser is using https.
CSS preprocessors help make authoring CSS easier. All of them offer things like variables and mixins to provide convenient abstractions.
It's a common practice to apply CSS to a page that styles elements such that they are consistent across all browsers. We offer two of the most popular choices: normalize.css and a reset. Or, choose Neither and nothing will be applied.
To get the best cross-browser support, it is a common practice to apply vendor prefixes to CSS properties and values that require them to work. For instance -webkit-
or -moz-
.
We offer two popular choices: Autoprefixer (which processes your CSS server-side) and -prefix-free (which applies prefixes via a script, client-side).
Any URL's added here will be added as <link>
s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.
You can apply CSS to your Pen from any stylesheet on the web. Just put a URL to it here and we'll apply it, in the order you have them, before the CSS in the Pen itself.
If the stylesheet you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the CSS from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
JavaScript preprocessors can help make authoring JavaScript easier and more convenient.
Babel includes JSX processing.
Any URL's added here will be added as <script>
s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.
You can apply a script from anywhere on the web to your Pen. Just put a URL to it here and we'll add it, in the order you have them, before the JavaScript in the Pen itself.
If the script you link to has the file extension of a preprocessor, we'll attempt to process it before applying.
You can also link to another Pen here, and we'll pull the JavaScript from that Pen and include it. If it's using a matching preprocessor, we'll combine the code before preprocessing, so you can use the linked Pen as a true dependency.
Search for and use JavaScript packages from npm here. By selecting a package, an import
statement will be added to the top of the JavaScript editor for this package.
Using packages here is powered by Skypack, which makes packages from npm not only available on a CDN, but prepares them for native JavaScript ES6 import
usage.
All packages are different, so refer to their docs for how they work.
If you're using React / ReactDOM, make sure to turn on Babel for the JSX processing.
If active, Pens will autosave every 30 seconds after being saved once.
If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.
If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.
Visit your global Editor Settings.
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- JavaScript -->
<script type="text/javascript" src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script type="text/javascript" src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" crossorigin="anonymous"></script>
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="https://use.fontawesome.com/releases/v5.13.1/css/all.css">
</head>
<body>
<div id="generator" class="container px-3 mt-4 mb-4">
<div class="card">
<div class="card-header font-weight-bold"><i class="fas fa-image"></i>Canvas に正しい向きで画像を描画する</div>
<div class="card-body px-3 px-md-5 pt-4">
<p>
<canvas id="canvas" width="1920" height="1080" style="display: block; background-color: #272727; width: 100%;"></canvas>
</p>
<p class="text-center text-md-left">
<input type="file" class="btn btn-info" accept="image/*" style="max-width: 100%; cursor: pointer;" />
</p>
<p class="mb-0 text-center text-md-left">
<button id="save" class="btn btn-info" style="cursor: pointer;">
<i class="fas fa-download"></i>
画像を保存する
</button>
</p>
</div>
</div>
</div>
</body>
</html>
body {
background-color: #dffbeb !important;
}
i {
width: 17px;
margin-right: 10px;
}
$(function(){
// Canvas を作っておく
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.fillStyle = '#272727';
// ファイルが選択された時
$('input[type=file]').change(function(event){
// ファイル情報を取得
file = event.target.files[0];
// キャンセルされたら戻る
if (file === undefined){
return;
}
// 画像ファイル以外だったらエラー出して戻る
if (!file.type.match('image.*')) {
alert('画像を選択してください!');
return;
}
// 選択されたファイルを読み込む
var reader = new FileReader();
// 読み込まれたら実行
reader.onloadend = function(){
// 画像が回転している場合は正しい向きに直す
clearOrientation(reader.result, function(result) {
// result: 横向きが直った base64 画像データ
// これを使って改めて Canvas を描画
picture = new Image();
picture.src = result;
// 回転が修正された画像を読み込む
picture.onload = function(event){
// アスペクト比にあわせて計算
// 画像の幅
var width = picture.naturalWidth;
// 画像の高さ
var height = picture.naturalHeight;
// アスペクト比
var aspect = height / width;
// キャンバスからはみ出ないように調整
// 16:9 より縦長なら
if (aspect >= 0.5625) { // 0.5625 … 16:9
console.log('縦長')
// 描画する幅
// 描画する高さ ÷ アスペクト比 = 描画する幅
var draw_width = canvas.height / aspect;
// 描画する高さ
var draw_height = canvas.height;
// 16:9 より縦長なら
} else if (aspect < 0.5625) {
console.log('横長')
// 描画する幅
var draw_width = canvas.width;
// 描画する高さ
// 描画する幅 × アスペクト比 = 描画する高さ
var draw_height = canvas.width * aspect;
}
// 左上からの幅
// Canvas の幅から描画幅を引いた残りの空間を半分に割る
var draw_left = (canvas.width - draw_width) / 2;
// 左上からの高さ
// Canvas の高さから描画高を引いた残りの空間を半分に割る
var draw_top = (canvas.height - draw_height) / 2;
// 予め塗りつぶしておく
context.fillRect(0, 0, canvas.width, canvas.height);
// 画像を描画する
context.drawImage(picture, draw_left, draw_top, draw_width, draw_height);
};
});
};
// 画像の読み込みを実行
if (file) {
reader.readAsDataURL(file);
}
});
});
// 保存する
$('#save').click(function(event){
if (canvas.msToBlob) {
var blob = canvas.msToBlob();
window.navigator.msSaveBlob(blob, 'image.png');
} else {
canvas.toBlob(function(blob){
var a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'image.png';
a.click();
});
}
});
// ブラウザが画像の自動回転機能をサポートしているか
// サポートしていれば true・サポートしていなければ false を返す
function isSupportedImageAutoRotation() {
// image-orientation: from-image; がサポートされた環境では image-orientation の既定値が from-image になっていることを利用
return getComputedStyle(document.body)['imageOrientation'] === 'from-image';
}
// 回転している画像を正しい向きに直す
// 参考: https://qiita.com/mo49/items/a3d61d97f1883ead333b
function clearOrientation(imgDataURL, callBack) {
var byteString = atob(imgDataURL.split(',')[1]);
var orientation = byteStringToOrientation(byteString);
// Exif の Orientation が存在している &
// Exif の Orientation が 1 以外 &
// ブラウザが画像の自動回転に対応していない場合 だけ画像を正しい向きに回転する
if (orientation !== undefined && orientation !== 1 && !isSupportedImageAutoRotation()){
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
switch (orientation) {
case 3: //画像が180度回転している時
canvas.width = img.width;
canvas.height = img.height;
ctx.rotate(Math.PI);
ctx.drawImage(img, -img.width, -img.height);
ctx.rotate(-Math.PI);
break;
case 6: //画像が時計回りに90度回っている時
canvas.width = img.height;
canvas.height = img.width;
ctx.rotate(Math.PI * 0.5);
ctx.drawImage(img, 0, -img.height);
ctx.rotate(-Math.PI * 0.5);
break;
case 8: //画像が反時計回りに90度回っている時
canvas.width = img.height;
canvas.height = img.width;
ctx.rotate(-Math.PI * 0.5);
ctx.drawImage(img, -img.width, 0);
ctx.rotate(Math.PI * 0.5);
break;
default:
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
}
callBack(canvas.toDataURL('image/jpeg'));
}
img.src = imgDataURL;
} else {
callBack(imgDataURL); // 渡された DataURL をそのまま返す
}
}
// Exif 情報の Orientation を取得する
// 参考: https://qiita.com/mo49/items/a3d61d97f1883ead333b
function byteStringToOrientation(img) {
var head = 0;
var orientation;
while (1) {
if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 218) {
break;
}
if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 216) {
head += 2;
} else {
var length = img.charCodeAt(head + 2) * 256 + img.charCodeAt(head + 3);
var endPoint = head + length + 2;
if (img.charCodeAt(head) == 255 & img.charCodeAt(head + 1) == 225) {
var segment = img.slice(head, endPoint);
var bigEndian = segment.charCodeAt(10) == 77;
if (bigEndian) {
var count = segment.charCodeAt(18) * 256 + segment.charCodeAt(19);
} else {
var count = segment.charCodeAt(18) + segment.charCodeAt(19) * 256;
}
for (var i = 0; i < count; i++) {
var field = segment.slice(20 + 12 * i, 32 + 12 * i);
if ((bigEndian && field.charCodeAt(1) == 18) || (!bigEndian && field.charCodeAt(0) == 18)) {
orientation = bigEndian ? field.charCodeAt(9) : field.charCodeAt(8);
}
}
break;
}
head = endPoint;
}
if (head > img.length) {
break;
}
}
return orientation;
}
Also see: Tab Triggers