web-dev-qa-db-ja.com

メディアアップローダを使用して特定のサイズの画像を選択し、トリミングを強化する

私は この記事 に従って、私のコードでMedia Uploaderを使用しました。フォームの[参照]ボタンをクリックすると、メディアアップローダが開き、画像を選択すると、その画像のURLがフォームフィールドに追加されます。今のところ問題ありません。

しかし、私はイメージが特定のサイズになるように強制したいです。サイズが異なる場合、ユーザーは必要なサイズ(または縦横比)に合わせてサイズをトリミングする必要があります 以前 画像を選択できます(そして私のmediaUploader.on('select', function() {が実行されます)。

add_image_sizeを使用したくないことに注意してください。アップロードされたそれぞれの画像ごとに、指定されたサイズのサムネイルが作成されるためです(私が間違っている場合は修正してください)。代わりに、特定のシナリオでのみ、アップロード中にユーザーに手動で画像をトリミングさせます。

基本的に私の要求はSOに関する質問でよく説明されています: wordpress-media-library-crop-insert-like-header-image-cropperの画像

しかし、そこに投稿された解決策はありません。だから私はWP専門家が私を導くためにここに置いています。

私は「ワードプレスメディアアップローダ(画像サイズを強制する|画像トリミング|特定のサイズの画像を選択する)」を検索してみましたが、そうではありません。しかし、どういうわけか、与えられた画像サイズを強制するためのステップを記述するリンクを得ていません。渡すことができるすべてのオプションを説明しているwp.mediaのドキュメントも見つかりませんでした。

私はすでに以下の質問を見ましたが、それは私が探していることをするようには思われません:

編集

私はWP_Customize_Cropped_Image_Control @ https://www.sitepoint.com/using-the-wordpress-customizer-media-controls/ の使用を見つけることができました - しかし、それはカスタマイザのコンテキスト内です。標準のプラグインで使用できる類似のコントロールはありますか?

そして、このwp.media.controller.Cropperwp-includes/js/media-views.jsの中に定義されていますが、どうやって使うのですか?

2
Vivek Athalye

選択およびトリミング機能は現在WordPressカスタマイザでのみ使用されているようです。

しかし、WordPressのAdminフォルダ内のコードを見た後、私はそれを私のテーマオプションページで動作させることができました。

これが私のヘッダー画像の設定です。

function setting_heading_picture() { 
  $heading_picture = esc_attr(get_option( 'heading_picture' )); ?>
  <input type="hidden" name="heading_picture" id="heading_picture" value="<?php echo $heading_picture; ?>" />
  <img id="heading_picture_preview" class="heading-picture" src="<?php echo $heading_picture; ?>" />
  <button id="btn_heading_picture" name="btn_heading_picture" class="button default">Choose Picture</button>
}

これが必要な管理用javascriptです。

$(function() {

function myTheme_calculateImageSelectOptions(attachment, controller) {

    var control = controller.get( 'control' );

    var flexWidth = !! parseInt( control.params.flex_width, 10 );
    var flexHeight = !! parseInt( control.params.flex_height, 10 );

    var realWidth = attachment.get( 'width' );
    var realHeight = attachment.get( 'height' );

    var xInit = parseInt(control.params.width, 10);
    var yInit = parseInt(control.params.height, 10);

    var ratio = xInit / yInit;

    controller.set( 'canSkipCrop', ! control.mustBeCropped( flexWidth, flexHeight, xInit, yInit, realWidth, realHeight ) );

    var xImg = xInit;
    var yImg = yInit;

    if ( realWidth / realHeight > ratio ) {
        yInit = realHeight;
        xInit = yInit * ratio;
    } else {
        xInit = realWidth;
        yInit = xInit / ratio;
    }        

    var x1 = ( realWidth - xInit ) / 2;
    var y1 = ( realHeight - yInit ) / 2;        

    var imgSelectOptions = {
        handles: true,
        keys: true,
        instance: true,
        persistent: true,
        imageWidth: realWidth,
        imageHeight: realHeight,
        minWidth: xImg > xInit ? xInit : xImg,
        minHeight: yImg > yInit ? yInit : yImg,            
        x1: x1,
        y1: y1,
        x2: xInit + x1,
        y2: yInit + y1
    };

    return imgSelectOptions;
}  

function myTheme_setImageFromURL(url, attachmentId, width, height) {
    var choice, data = {};

    data.url = url;
    data.thumbnail_url = url;
    data.timestamp = _.now();

    if (attachmentId) {
        data.attachment_id = attachmentId;
    }

    if (width) {
        data.width = width;
    }

    if (height) {
        data.height = height;
    }

    $("#heading_picture").val( url );
    $("#heading_picture_preview").prop("src", url);        

}

function myTheme_setImageFromAttachment(attachment) {

    $("#heading_picture").val( attachment.url );
    $("#heading_picture_preview").prop("src", attachment.url);             

}

var mediaUploader;

$("#btn_heading_picture").on("click", function(e) {

    e.preventDefault(); 

    /* We need to setup a Crop control that contains a few parameters
       and a method to indicate if the CropController can skip cropping the image.
       In this example I am just creating a control on the fly with the expected properties.
       However, the controls used by WordPress Admin are api.CroppedImageControl and api.SiteIconControl
    */

   var cropControl = {
       id: "control-id",
       params : {
         flex_width : false,  // set to true if the width of the cropped image can be different to the width defined here
         flex_height : true, // set to true if the height of the cropped image can be different to the height defined here
         width : 300,  // set the desired width of the destination image here
         height : 200, // set the desired height of the destination image here
       }
   };

   cropControl.mustBeCropped = function(flexW, flexH, dstW, dstH, imgW, imgH) {

    // If the width and height are both flexible
    // then the user does not need to crop the image.

    if ( true === flexW && true === flexH ) {
        return false;
    }

    // If the width is flexible and the cropped image height matches the current image height, 
    // then the user does not need to crop the image.
    if ( true === flexW && dstH === imgH ) {
        return false;
    }

    // If the height is flexible and the cropped image width matches the current image width, 
    // then the user does not need to crop the image.        
    if ( true === flexH && dstW === imgW ) {
        return false;
    }

    // If the cropped image width matches the current image width, 
    // and the cropped image height matches the current image height
    // then the user does not need to crop the image.               
    if ( dstW === imgW && dstH === imgH ) {
        return false;
    }

    // If the destination width is equal to or greater than the cropped image width
    // then the user does not need to crop the image...
    if ( imgW <= dstW ) {
        return false;
    }

    return true;        

   };      

    /* NOTE: Need to set this up every time instead of reusing if already there
             as the toolbar button does not get reset when doing the following:

            mediaUploader.setState('library');
            mediaUploader.open();

    */       

    mediaUploader = wp.media({
        button: {
            text: 'Select and Crop', // l10n.selectAndCrop,
            close: false
        },
        states: [
            new wp.media.controller.Library({
                title:     'Select and Crop', // l10n.chooseImage,
                library:   wp.media.query({ type: 'image' }),
                multiple:  false,
                date:      false,
                priority:  20,
                suggestedWidth: 300,
                suggestedHeight: 200
            }),
            new wp.media.controller.CustomizeImageCropper({ 
                imgSelectOptions: myTheme_calculateImageSelectOptions,
                control: cropControl
            })
        ]
    });

    mediaUploader.on('cropped', function(croppedImage) {

        var url = croppedImage.url,
            attachmentId = croppedImage.attachment_id,
            w = croppedImage.width,
            h = croppedImage.height;

            myTheme_setImageFromURL(url, attachmentId, w, h);            

    });

    mediaUploader.on('skippedcrop', function(selection) {

        var url = selection.get('url'),
            w = selection.get('width'),
            h = selection.get('height');

            myTheme_setImageFromURL(url, selection.id, w, h);            

    });        

    mediaUploader.on("select", function() {

        var attachment = mediaUploader.state().get( 'selection' ).first().toJSON();

        if (     cropControl.params.width  === attachment.width 
            &&   cropControl.params.height === attachment.height 
            && ! cropControl.params.flex_width 
            && ! cropControl.params.flex_height ) {
                myTheme_setImageFromAttachment( attachment );
            mediaUploader.close();
        } else {
            mediaUploader.setState( 'cropper' );
        }

    });

    mediaUploader.open();

});
});

上記のコードで必要な幅/高さを調整することを忘れないでください。

トリミングした画像を添付ファイルとして保存するためのコードは省略されています。トリミングされた画像はwp-content/uploadsの通常の場所にありますが、メディアライブラリにはトリミングされた画像は表示されません。

私はクロッパーで正確なサイズを強制する方法を知りません。うまく行けば、他の誰かが一緒に来て、それに答えるのを手伝うことができます。

2
Darren

どのページでも最初に@Darrenによって与えられたコードを使用してください。簡単にするために、PHPとJSコードを1つのブロックにまとめました。

  <?php $heading_picture = esc_attr(get_option( 'heading_picture' )); ?>
  <input type="hidden" name="heading_picture" id="heading_picture" value="<?php echo $heading_picture; ?>" />
  <img id="heading_picture_preview" class="heading-picture" src="<?php echo $heading_picture; ?>" />
  <button id="btn_heading_picture" name="btn_heading_picture" class="button default">Choose Picture</button>
<script>
jQuery(function($) {

function myTheme_calculateImageSelectOptions(attachment, controller) {

    var control = controller.get( 'control' );

    var flexWidth = !! parseInt( control.params.flex_width, 10 );
    var flexHeight = !! parseInt( control.params.flex_height, 10 );

    var realWidth = attachment.get( 'width' );
    var realHeight = attachment.get( 'height' );

    var xInit = parseInt(control.params.width, 10);
    var yInit = parseInt(control.params.height, 10);

    var ratio = xInit / yInit;

    controller.set( 'canSkipCrop', ! control.mustBeCropped( flexWidth, flexHeight, xInit, yInit, realWidth, realHeight ) );

    var xImg = xInit;
    var yImg = yInit;

    if ( realWidth / realHeight > ratio ) {
        yInit = realHeight;
        xInit = yInit * ratio;
    } else {
        xInit = realWidth;
        yInit = xInit / ratio;
    }        

    var x1 = ( realWidth - xInit ) / 2;
    var y1 = ( realHeight - yInit ) / 2;        

    var imgSelectOptions = {
        handles: true,
        keys: true,
        instance: true,
        persistent: true,
        imageWidth: realWidth,
        imageHeight: realHeight,
        minWidth: xImg > xInit ? xInit : xImg,
        minHeight: yImg > yInit ? yInit : yImg,            
        x1: x1,
        y1: y1,
        x2: xInit + x1,
        y2: yInit + y1
    };

    return imgSelectOptions;
}  

function myTheme_setImageFromURL(url, attachmentId, width, height) {
    var choice, data = {};

    data.url = url;
    data.thumbnail_url = url;
    data.timestamp = _.now();

    if (attachmentId) {
        data.attachment_id = attachmentId;
    }

    if (width) {
        data.width = width;
    }

    if (height) {
        data.height = height;
    }

    $("#heading_picture").val( url );
    $("#heading_picture_preview").prop("src", url);        

}

function myTheme_setImageFromAttachment(attachment) {

    $("#heading_picture").val( attachment.url );
    $("#heading_picture_preview").prop("src", attachment.url);             

}

var mediaUploader;

$("#btn_heading_picture").on("click", function(e) {

    e.preventDefault(); 

    /* We need to setup a Crop control that contains a few parameters
       and a method to indicate if the CropController can skip cropping the image.
       In this example I am just creating a control on the fly with the expected properties.
       However, the controls used by WordPress Admin are api.CroppedImageControl and api.SiteIconControl
    */

   var cropControl = {
       id: "control-id",
       params : {
         flex_width : false,  // set to true if the width of the cropped image can be different to the width defined here
         flex_height : true, // set to true if the height of the cropped image can be different to the height defined here
         width : 300,  // set the desired width of the destination image here
         height : 200, // set the desired height of the destination image here
       }
   };

   cropControl.mustBeCropped = function(flexW, flexH, dstW, dstH, imgW, imgH) {

    // If the width and height are both flexible
    // then the user does not need to crop the image.

    if ( true === flexW && true === flexH ) {
        return false;
    }

    // If the width is flexible and the cropped image height matches the current image height, 
    // then the user does not need to crop the image.
    if ( true === flexW && dstH === imgH ) {
        return false;
    }

    // If the height is flexible and the cropped image width matches the current image width, 
    // then the user does not need to crop the image.        
    if ( true === flexH && dstW === imgW ) {
        return false;
    }

    // If the cropped image width matches the current image width, 
    // and the cropped image height matches the current image height
    // then the user does not need to crop the image.               
    if ( dstW === imgW && dstH === imgH ) {
        return false;
    }

    // If the destination width is equal to or greater than the cropped image width
    // then the user does not need to crop the image...
    if ( imgW <= dstW ) {
        return false;
    }

    return true;        

   };      

    /* NOTE: Need to set this up every time instead of reusing if already there
             as the toolbar button does not get reset when doing the following:

            mediaUploader.setState('library');
            mediaUploader.open();

    */       

    mediaUploader = wp.media({
        button: {
            text: 'Select and Crop', // l10n.selectAndCrop,
            close: false
        },
        states: [
            new wp.media.controller.Library({
                title:     'Select and Crop', // l10n.chooseImage,
                library:   wp.media.query({ type: 'image' }),
                multiple:  false,
                date:      false,
                priority:  20,
                suggestedWidth: 300,
                suggestedHeight: 200
            }),
            new wp.media.controller.CustomizeImageCropper({ 
                imgSelectOptions: myTheme_calculateImageSelectOptions,
                control: cropControl
            })
        ]
    });

    mediaUploader.on('cropped', function(croppedImage) {

        var url = croppedImage.url,
            attachmentId = croppedImage.attachment_id,
            w = croppedImage.width,
            h = croppedImage.height;

            myTheme_setImageFromURL(url, attachmentId, w, h);            

    });

    mediaUploader.on('skippedcrop', function(selection) {

        var url = selection.get('url'),
            w = selection.get('width'),
            h = selection.get('height');

            myTheme_setImageFromURL(url, selection.id, w, h);            

    });        

    mediaUploader.on("select", function() {

        var attachment = mediaUploader.state().get( 'selection' ).first().toJSON();

        if (     cropControl.params.width  === attachment.width 
            &&   cropControl.params.height === attachment.height 
            && ! cropControl.params.flex_width 
            && ! cropControl.params.flex_height ) {
                myTheme_setImageFromAttachment( attachment );
            mediaUploader.close();
        } else {
            mediaUploader.setState( 'cropper' );
        }

    });

    mediaUploader.open();

});
});
</script>

それからいくつかの plugin で次のコードを使ってください:

add_action( 'wp_enqueue_scripts', 'vna_wp_enqueue_scripts' );
function vna_wp_enqueue_scripts() {
    wp_enqueue_media();
    wp_enqueue_script( 'imgareaselect', get_bloginfo('url') . '/wp-includes/js/imgareaselect/jquery.imgareaselect.js', array( 'jquery' ), '1', true );
    wp_enqueue_style( 'imgareaselect', get_bloginfo('url') . '/wp-includes/js/imgareaselect/imgareaselect.css', array(), '0.9.8' );
}
add_action( 'setup_theme','vna_wp_ajax_crop_image', 1 );
function vna_wp_ajax_crop_image() {
    global $wp_customize;
    if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'crop-image') {
        $post = new WP_Post(new stdClass());
        $post->ID = $_REQUEST['id'];
        $post->post_type = 'customize_changeset';
        $post->filter = 'raw';

        wp_cache_set($wp_customize->changeset_uuid(), $post, 'customize_changeset_post');
    }
}

注:

  1. Admin JSファイルへの依存度が非常に高いです。以前は、テスト中に発生していたJSエラーに基づいて、これらのファイルを次々に追加しようとしていました。それから私はwp_enqueue_media()を見つけました。それはほとんどの仕事をします。まだimgareaselect JSとCSSはロードされていないので、明示的にロードする必要があります。
  2. 上記のコードは プラグイン の中になければなりません。テーマ内のコードを試していましたが、テーマ内のsetup_themeアクションが予定されているイベントの後に呼び出されるため、機能しませんでした(後述)。それを避けるために私はそれをプラグインに入れなければなりませんでした。
  3. 切り取り機能は、ユーザーがログインしていて、そのイメージに対する edit_post 権限を持っている場合にのみ機能します。そうでなければ、アクションが0のときにadmin-ajax.phpからの応答としてcrop-imageを受け取ります。
  4. ハッキーな方法setup_themeアクションで上記のコードを実行し、優先度を1に設定します。これはclass WP_Customize_Managerが実行され、タイプsetup_themeの投稿ID /投稿の存在をチェックするcustomize_changesetメソッドを持っているからです。また、投稿フィルタはrawである必要があります。そうでない場合、指定された投稿IDに対してDBから投稿をロードしようとします(この場合、投稿IDとして画像IDを設定します)。その結果、投稿タイプはattachmentに設定されます。チェックが失敗した場合(タイプがcustomize_changesetの特定のIDの投稿が見つからない場合)、アクションが-1の場合、admin-ajax.phpからの応答としてcrop-imageを受け取ります。 DBに有効なチェンジセットの投稿を作成する代わりに、ダミーの投稿オブジェクトを作成してキャッシュに追加しました。

コードはあまり見かけませんが、 my comment で前述したように、これらすべての条件を見つけるのは本当に面倒でした。私はそれをafrallに管理できてうれしいです。

2
Vivek Athalye