検索
ホームページウェブフロントエンドjsチュートリアルImageZoom画像拡大鏡エフェクト(多機能拡張)_JavaScriptスキル

主に元の画像と表示フレームの表示モードを拡張し、次のモードがあります。
「フォロー」フォロー モード: 表示フレームはマウスの動きの効果に追従できます。
「ハンドル」ドラッグ ハンドル モード:表示範囲をマークするためのドラッグハンドルがあります。
「クロッパー」カットモード: 元の画像は表示範囲をマークするために不透明になり、他の部分は半透明で表示されます。
「ハンドル-クロッパー」ドラッグハンドルカットモード: ドラッグ ハンドル モードとカット モードのハイブリッド バージョン。透明度とドラッグ ハンドルを使用して表示範囲をマークします。
もちろん、さらに多くの拡張機能があなたの想像力の探索を待っています。
互換性: ie6/7/8、firefox 3.6.2、opera 10.51、safari 4.0.4、chrome 4.1

プログラムの説明

【 拡張モード 】

前回ImageLazyLoadは拡張に継承を使用しましたが、今回はプラグインを使用して拡張します。

最初に基本モードを見てみましょう。これらのモードは、次のような構造で ImageZoom._MODE に保存されます。コードをコピーします

コードは次のとおりです: ImageZoom._MODE = { モード名: { オプション: { ...
}、
メソッド: {
init: function() {
...
}、
...
}
}、
。 ..
}


モード名は基本モードの名前、オプションはオプションのパラメーター拡張子、メソッドはプログラム構造の拡張子です。
基本モードには「フォロー」、「ハンドル」、「クロッパー」モードがあり、これらについては後ほど詳しく紹介します。
メソッドには拡張するフックプログラムが含まれており、拡張の主要部分です。
ps: ここで説明するパターンは、「デザインパターン」のパターンではありません。
展開はプログラムの初期化中に実行する必要があり、_initialize プログラムの前に実行する必要があります。
元のプログラムの構造に影響を与えないように、ウィービング手法を使用して _initialize の前にプログラムを挿入します:





コードをコピー

コードは次のとおりです。 ImageZoom.prototype._initialize = (function(){ var init = ImageZoom.prototype._initialize, ... ; return function (){
...
init.apply(this, argument )
}
})();まず元の関数を保存し、プログラムを挿入して新しい関数を作成し、次に元の関数を置き換えます。

基本モードの組み合わせを考慮して、実際に使用されるモードを保存するためにオブジェクトが使用されます:




コードをコピー


コードは次のとおりです:

mode = ImageZoom._MODE, modes = { "follow": [ mode.follow ], "handle": [ モード.ハンドル ], "クロッパー": [ モード.クロッパー ], "ハンドル-クロッパー": [ モード.ハンドル, モード.クロッパー ]
}; >閲覧可能 「ハンドルクロッパー」モードは、実際には「ハンドル」と「クロッパー」を組み合わせたモードです。

挿入されたプログラムの主なタスクは、設定された基本モードに従って展開することです:




コードをコピー


コードは次のとおりです:


var options = argument[2];
if ( options && options.mode && modes[ options.mode ] ) {
$$A.forEach( modes[ options.mode ], function( mode ){
$$.extend( options, mode.options, false ); $$A.forEach( mode.methods, function( method, name ){ $$CE.addEvent( this, name, メソッド ); }, this );
まずoptionsのオプションパラメータオブジェクトを展開します。オプションパラメータは3番目のパラメータなのでarguments[2]で取得します。
extend の 3 番目のパラメーターは false に設定されます。これは、同じ属性が書き換えられない、つまり、カスタマイズされた属性値が保持されることを意味します。
次に、メソッド内のメソッドをフック関数としてプログラムに 1 つずつ追加します。

メソッドには、init、load、start、repair、move、end、dispose を含めることができます。これらは、ImageZoom の初期化、load、start、modification、move、end、destruction イベントにそれぞれ対応します。
展開中、さまざまなイベントがさまざまなタスクを実行します。
init 初期化関数: 展開に必要な属性を設定するために使用されます。これらの属性は ImageZoom 自体の属性と競合しない、つまり同じ名前を持つことに注意してください。
load 読み込み関数: 画像の読み込みが完了し、関連するパラメータも設定されます。これは主に拡大効果の準備に使用されます。
start関数: 増幅効果がトリガーされたときに実行されます。
修復補正機能: 大きな画像の位置決めの座標値を補正するために使用されます。
move 関数: 拡大効果をトリガーした後にマウスが移動すると実行されます。
終了関数は: 拡大効果を終了するときに実行されます。
dispose 破壊関数: プログラムを削除するときにプログラムをクリーンアップします。
追記: 特定の場所については、ImageZoom で $$CE.fireEvent を使用するセクションを参照してください。

ウィーブとフックを使用してプログラムを拡張していることがわかります。
Weaving メソッドは AOP の一種で、元のプログラムを変更せずに拡張できますが、関数の前後にプログラムを追加することしかできません。
フック メソッドを一緒に使用するには、元のプログラムで対応するフックを設定する必要がありますが、その位置は比較的柔軟です。


【フォローモード】

「フォロー」モードでは、ズームインすると、虫眼鏡を持っているように、マウスの動きに表示枠が追従します。

まず、表示フレームがマウスの動きに追従するようにするには、表示フレームを絶対的に配置する必要があります。


で、対応する左/上を設定するだけです。 var style = this._viewer.style;
style.left = e.pageX - this._repairFollowLeft "px";
style.top = e.pageY - this._repairFollowTop "px"; /pageY はマウスの現在の座標、_repairFollowLeft/_repairFollowTop は座標の補正パラメータです。

表示ボックスが非表示の場合は、前回の表示範囲の取得方法でパラメータを取得します:

コードをコピーします コードは次のとおりです:
if ( !viewer.offsetWidth ) {
styles = { display: style.display, Visibility : style.visibility };
$$D.setStyle( viewer, { 表示: "ブロック"、可視性: "非表示" });
}
...
if (styles) $$D.setStyle( viewer,styles ); }

以下のように、マウスを表示フレームの中央に固定し、最初にパラメータの offsetWidth/offsetHeight に従って修正します。表示フレーム:

コードをコピー コードは次のとおりです:
this._repairFollowLeft = viewer .offsetWidth / 2;
this._repairFollowTop = viewer.offsetHeight / 2;

表示ボックスの offsetParent が body でない場合は、offsetParent に基づいて座標を修正する必要があります。

コードをコピー コードは次のとおりです。
if ( !/BODY|HTML/. test( viewer.offsetParent.nodeName ) ) {
varparent = viewer.offsetParent, rect = $$D.rect(parent );
this._repairFollowLeft = rect.leftparent.clientLeft;
this. _repairFollowTop = rect.topparent.clientTop;
}

ps: Maxthon テスト中に body が見つかりました 要素の offsetParent は body ではなく html です。

プログラムを削除した後に表示ボックスのスタイルを復元するために、ロード プログラムでスタイルのバックアップが作成されます:

コードをコピー コードは次のとおりです:
var viewer = this._viewer, style = viewer.style,styles;
this._stylesFollow = {
left: style.left、top: style.top、position: style.position


、dispose:

$$D.setStyle( this._viewer, this._stylesFollow );

これで基本的な効果は得られましたが、大きな画像の移動範囲に制限があるため、マウスが境界線に近づくと大きな画像が引っかかってしまいます。動かない。
マウスが移動すると大きな画像が変化し続けるという効果を実現するために、移動座標が修復で修正されました:


コードをコピー コードは次のとおりです:

pos.left = ( viewerWidth / 2 - pos.left ) * ( viewerWidth /zoom.width - 1 );
pos.top = ( viewerHeight / 2 - pos.top ) * ( viewerHeight / zoom.height - 1 );

原理は少し複雑です。水平座標を例として、下の図を見てください。
ImageZoom画像拡大鏡エフェクト(多機能拡張)_JavaScriptスキル
大きなボックスは大きな画像オブジェクト、小さなボックス Frame は表示フレームを表します。
現在位置はマウス座標に基づいて取得される実際の表示位置であり、ターゲット位置は効果が達成される位置です。
物理学または幾何学の知識があれば、次の方程式を理解できるはずです: x / y = m / n
次のように推測できます: y = x * n / m = x * (zoom.width - viewerWidth ) /zoom.height
x の現在の座標は pos.left で取得できます: x = viewerWidth / 2 - pos.left
最後に、 left = -y = (viewerWidth / 2 - pos.left ) * ( viewerWidth / zoom.width - 1)
垂直方向の座標は似ています。


[ドラッグハンドルモード]

ドラッグハンドルは、元画像の上にあるレイヤーで、元画像内の表示範囲の位置と範囲を示すために使用されます。
表示範囲は_rangeWidth/_rangeHeightに基づいて取得できます。
位置指定は、マウス座標または大きな画像の配置座標に従って設定できます。
マウス座標を使用する場合、範囲制御などの他の処理を行う必要がありますが、大きな画像に基づいて座標を配置する方が比較的便利で正確です。プログラムでも後者の方法を使用します。

最初に init で _handle ドラッグ ハンドル オブジェクトを定義します:
コードをコピーします コードは次のとおりです:

var handle = $$( this.options.handle );
if ( !handle ) {
var body = document.body
handle = body.insertBefore(this) ._viewer.cloneNode(false), body.childNodes[0]);
handle.id = "";
handle["_createbyhandle"] = true; ( handle, { padding: 0, margin: 0, display: "none" } );


カスタムのドラッグ ハンドル オブジェクトがない場合は、表示ボックスがドラッグ ハンドル オブジェクトとしてコピーされます。
自動生成されたドラッグ ハンドル オブジェクトには、破棄時に削除しやすくするためのマークとして「_createbyhandle」属性が追加されます。

ロード時に、ドラッグ ハンドル スタイルを設定します:


コードをコピーします コードは次のとおりです:
$$D.setStyle( handle, {
position: "absolute",
width: this._rangeWidth "px",
height: this._rangeHeight "px",
display: "block",
visibility: "hidden"
});


絶対位置指定が必要であり、サイズは _rangeWidth/_rangeHeight に従って設定されます。
表示・可視性の設定は以下のパラメータを取得します。

まず、元の画像の座標に基づいて補正パラメータを取得します。


this._repairHandleLeft = rect.left this._repairLeft - handle.clientLeft
this._repairHandleTop = rect; .top this ._repairTop - handle.clientTop;

フォロー モードと同様に、offsetParent の位置も修正する必要があります:



if (handle.offsetParent.nodeName.toUpperCase() != "BODY" ) {
varparent = handle.offsetParent 、rect = $$D .rect(parent);
this._repairHandleLeft -= rect.leftparent.clientLeft;
this._repairHandleTop -= rect.topparent.clientTop; 🎜>

その後、再度非表示にします:

$$D.setStyle( handle, { display: "none", Visibility: "visible" }); 、ドラッグ ハンドル オブジェクトを表示します。
移動するときは、大きな画像の位置座標に従ってドラッグ ハンドルの位置を設定します:





コードをコピー


コードは次のとおりです: 最後に、ドラッグ ハンドル オブジェクトを非表示にします。


【カットモード】

「カット」とは、選択した部分を完全に透明にし、その他の部分を半透明にする効果です。
主にクリッピングによって実現されますが、特定の原理は画像のカット効果で見ることができます。

カット効果を実現するには、init で新しい _cropper カッティング レイヤーを作成する必要があります:





コードをコピー


コードは次のとおりです:
var body = document.body, cropper = body.insertBefore(document.createElement("img"), body.childNodes[0 ]); クロッパー .style.display = "なし";
そして、この切断レイヤーをロードに設定します:

コードをコピーします コードは次のとおりです:

cropper.src = image.src;
cropper.width = image.width;
$$D.setStyle( Cropper, {
位置: "絶対",
left:rect.left this._repairLeft "px",
top:rect.top this._repairTop "px"
}); >元の画像オブジェクトをほぼコピーし、元の画像オブジェクトの上に完全に配置します。

起動時に切り抜きレイヤーを表示し、透明度に合わせて元画像を半透明状態にします。

移動する場合、大きな画像の移動の座標に応じて切り取りレイヤーのクリッピング範囲を設定します:




コードをコピー
コードは次のとおりです。 var w = this._rangeWidth、h = this._rangeHeight、scale = this._scale x = Math.ceil( -x /scale; ); y = Math.( -y / スケール );
this._cropper.style.clip = "rect(" y "px " (x w) "px " (y h)" ;


最後に、切り抜きレイヤーを非表示にし、元の画像を不透明にリセットして元の状態に戻します。

廃棄の際は、切断層も忘れずに取り除いてください。


使用上のヒント

この拡張機能は拡張効果が必要な場合にのみ追加する必要があります。

ImageZoom._MODE は自分で展開できます。展開後、必ず対応するモードをモードに追加してください。

複数の基本モードを組み合わせて同時に使用することができます。詳細については、「ハンドルクロッパー」モードを参照してください。



使用説明


使用方法は ImageZoom と似ていますが、表示モードを設定する追加のオプションの参照モードがある点が異なります。 「ハンドル」モードを使用する場合、オプションのパラメータの「ハンドル」属性でドラッグ ハンドル オブジェクトを設定できます。 「クロッパー」モードを使用する場合、オプションのパラメーターの「不透明度」属性で透明度を設定できます。
「ハンドルクロッパー」モードを使用する場合、上記のパラメータの両方を使用できます。

プログラムのソースコード



コードをコピー
コードは次のとおりです。

ImageZoom._MODE = {
//Follow
"follow": {
methods: {
init: function() {
this._stylesFollow = null;/ /バックアップ スタイル
this._repairFollowLeft = 0;//左の補正座標
this._repairFollowTop = 0;//上側の補正座標
},
load: function() {
var viewer = this._viewer、style = viewer.style、styles;
this._stylesFollow = {
left: style.left、top: style.top、position: style.position
}; .style.position = "absolute";
//補正パラメータを取得します
if ( !viewer.offsetWidth ) {//Hide
styles = { display: style.display, Visibility: style.visibility } ;
$$D.setStyle(viewer, { display: "block", Visibility: "hidden" });
}
//中心位置を修正
this._repairFollowLeft = viewer.offsetWidth / 2 ;
this._repairFollowTop = viewer.offsetHeight / 2;
//オフセット親の位置を修正します
if ( !/BODY|HTML/.test( viewer.offsetParent.nodeName ) ) {
var parent = viewer.offsetParent、rect = $$D.rect(parent );
this._repairFollowLeft = rect.leftparent.clientLeft;
this._repairFollowTop = rect.topparent.clientTop; 🎜> if (styles) { $$D.setStyle(viewer,styles); }
},
repair: function(e, pos) {
varzoom = this._zoom,
viewerWidth = this ._viewerWidth,
viewerHeight = this._viewerHeight;
pos.left = ( viewerWidth / 2 - pos.left ) * ( viewerWidth /zoom.width - 1 ); / 2 - pos.top ) * ( viewerHeight /zoom.height - 1 );
},
move: function(e) {
var style = this._viewer.style; left = e.pageX - this._repairFollowLeft "px";
style.top = e.pageY - this._repairFollowTop "px"
},
dispose: function() {
$$ D. setStyle( this._viewer, this._stylesFollow );
}
}
},
//ドラッグ ハンドル
"handle": {
options: {//デフォルトvalue
handle: ""//ドラッグ ハンドル オブジェクト
},
methods: {
init: function() {
var handle = $$( this.options.handle ); 🎜> if ( !handle ) {//定義されていない場合は、表示ボックスをコピーで置き換えます
var body = document.body;
handle = body.insertBefore(this._viewer.cloneNode(false), body) .childNodes[0] ; 0, margin: 0, display: "none" } );

this._handle = handle;
this._repairHandleLeft = 0;//左の座標._repairHandleTop = 0;/ /修正トップ
},
load: function() {
var handle = this._handle, rect = this._rect;
$$D.setStyle( handle, {
位置 : "絶対"、
幅: this._rangeWidth "px"、
高さ: this._rangeHeight "px"、
表示: "ブロック"、
可視性: "非表示"
} );
//補正パラメータを取得します
this._repairHandleLeft = rect.left this._repairLeft - handle.clientLeft;
this._repairHandleTop = rect.top this._repairTop - handle. clientTop;
/ /オフセット親の位置を修正します
if ( !/BODY|HTML/.test( handle.offsetParent.nodeName ) ) {
varparent = handle.offsetParent, rect = $$D.rect(親);
this._repairHandleLeft -= rect.leftparent.clientLeft;
this._repairHandleTop -= rect.topparent.clientTop;
//Hide
$$D. setStyle( handle, { 表示 : "なし"、可視性: "表示" });
},
start: function() {
this._handle.style.display = "ブロック"; >},
move: function(e, x, y) {
var style = this._handle.style,scale = this._scale;
style.left = Math.ceil( this._repairHandleLeft - x / スケール ) "px ";
style.top = Math.ceil(this._repairHandleTop - y / スケール ) "px";
},
end: function() {
this. _handle.style.display = "none";
},
dispose: function() {
if( "_createbyhandle" in this._handle ){ document.body.removeChild( this._handle );
this._handle = null;
}
}
},
//Cut
"cropper": {
options: {//デフォルト値
不透明度: .5//透明性
},
メソッド: {
init: function() {
var body = document.body,
cropper = body.insertBefore(document.createElement(" img"), body.childNodes[0]);
cropper.style.display = "none";

this._cropper = Cropper;
this.opacity = this.options.opacity;
}、
load: function() {
var Cropper = this._cropper、image = this._image、rect = this._rect;
cropper.src = image.src;クロッパー.幅 = 画像.幅;
クロッパー.高さ = 画像.高さ;
位置: "絶対",
左: これ。 _repairLeft "px",
top: rect.top this._repairTop "px"
});
},
start: function() {
this._cropper.style.display = "ブロック";
$$D.setStyle(this._image, "opacity", this.opacity );
move: function(e, x, y) {
var w = this._rangeWidth, h = this._rangeHeight, スケール = this._scale;
x = Math.ceil( -x / スケール ) y = Math.ceil( -y / スケール ); .style.clip = "rect(" y "px " (x w) "px " (y h) "px " x "px)";,
end: function() {
$$D.setStyle( this._image, "opacity", 1 );
this._cropper.style.display = "なし";
},
dispose: function() {
document.body.removeChild( this._cropper );
this._cropper = null;
}
}
}
}

ImageZoom.prototype._initialize = (function(){
var init = ImageZoom.prototype._initialize,
mode = ImageZoom._MODE,
modes = {
"follow": [ mode.follow ],
"handle": [ mode.handle ],
"cropper": [ mode.cropper ],
"handle-cropper": [mode.handle, mode.cropper ]
};
return function(){
var options = argument[2];
if ( options && options. mode && modes[ options.mode ] ) {
$$A.forEach( modes[ options.mode ], function( mode ){
//扩展options
$$.extend( options, mode. options, false );
//扩展钩子
$$A.forEach(mode.methods, function(method, name ){
$$CE.addEvent(this, name, method );
}, this );
}, this );
init.apply( this, argument )
}
});地址
http://demo.jb51.net/js/ImageZoom_ext/ImageZoom_ext.htm

打包下ダウンロード地址
http://www.jb51.net/jiaoben/25809.html
出处:http://www.cnblogs.com/cloudgamer/
声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
JavaScriptの文字列文字を交換しますJavaScriptの文字列文字を交換しますMar 11, 2025 am 12:07 AM

JavaScript文字列置換法とFAQの詳細な説明 この記事では、javaScriptの文字列文字を置き換える2つの方法について説明します:内部JavaScriptコードとWebページの内部HTML。 JavaScriptコード内の文字列を交換します 最も直接的な方法は、置換()メソッドを使用することです。 str = str.replace( "find"、 "置換"); この方法は、最初の一致のみを置き換えます。すべての一致を置き換えるには、正規表現を使用して、グローバルフラグGを追加します。 str = str.replace(/fi

カスタムGoogle検索APIセットアップチュートリアルカスタムGoogle検索APIセットアップチュートリアルMar 04, 2025 am 01:06 AM

このチュートリアルでは、カスタムGoogle検索APIをブログまたはWebサイトに統合する方法を示し、標準のWordPressテーマ検索関数よりも洗練された検索エクスペリエンスを提供します。 驚くほど簡単です!検索をyに制限することができます

独自のAjax Webアプリケーションを構築します独自のAjax Webアプリケーションを構築しますMar 09, 2025 am 12:11 AM

それで、あなたはここで、Ajaxと呼ばれるこのことについてすべてを学ぶ準備ができています。しかし、それは正確には何ですか? Ajaxという用語は、動的でインタラクティブなWebコンテンツを作成するために使用されるテクノロジーのゆるいグループ化を指します。 Ajaxという用語は、もともとJesse Jによって造られました

例JSONファイルの例例JSONファイルの例Mar 03, 2025 am 12:35 AM

この記事シリーズは、2017年半ばに最新の情報と新鮮な例で書き直されました。 このJSONの例では、JSON形式を使用してファイルに単純な値を保存する方法について説明します。 キー価値ペア表記を使用して、あらゆる種類を保存できます

8見事なjQueryページレイアウトプラグイン8見事なjQueryページレイアウトプラグインMar 06, 2025 am 12:48 AM

楽なWebページレイアウトのためにjQueryを活用する:8本質的なプラグイン jQueryは、Webページのレイアウトを大幅に簡素化します。 この記事では、プロセスを合理化する8つの強力なjQueryプラグイン、特に手動のウェブサイトの作成に役立ちます

' this' JavaScriptで?' this' JavaScriptで?Mar 04, 2025 am 01:15 AM

コアポイント これは通常、メソッドを「所有」するオブジェクトを指しますが、関数がどのように呼び出されるかに依存します。 現在のオブジェクトがない場合、これはグローバルオブジェクトを指します。 Webブラウザでは、ウィンドウで表されます。 関数を呼び出すと、これはグローバルオブジェクトを維持しますが、オブジェクトコンストラクターまたはそのメソッドを呼び出すとき、これはオブジェクトのインスタンスを指します。 call()、apply()、bind()などのメソッドを使用して、このコンテキストを変更できます。これらのメソッドは、与えられたこの値とパラメーターを使用して関数を呼び出します。 JavaScriptは優れたプログラミング言語です。数年前、この文はそうでした

ソースビューアーでjQueryの知識を向上させますソースビューアーでjQueryの知識を向上させますMar 05, 2025 am 12:54 AM

jQueryは素晴らしいJavaScriptフレームワークです。ただし、他のライブラリと同様に、何が起こっているのかを発見するためにフードの下に入る必要がある場合があります。おそらく、バグをトレースしているか、jQueryが特定のUIをどのように達成するかに興味があるからです

モバイル開発用のモバイルチートシート10個モバイル開発用のモバイルチートシート10個Mar 05, 2025 am 12:43 AM

この投稿は、Android、BlackBerry、およびiPhoneアプリ開発用の有用なチートシート、リファレンスガイド、クイックレシピ、コードスニペットをコンパイルします。 開発者がいないべきではありません! タッチジェスチャーリファレンスガイド(PDF) Desigの貴重なリソース

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

MantisBT

MantisBT

Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)