React-dnd: لا يؤدي توصيل monitor.getClientOffset إلى DropTarget إلى تحديث الدعائم

تم إنشاؤها على ٣ يونيو ٢٠١٥  ·  22تعليقات  ·  مصدر: react-dnd/react-dnd

لقد أنشأت DropTarget وقمت بتوصيل getClientOffset على النحو التالي:

targetCollect = function (connect, monitor) {
  return {
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
    clientOffset: monitor.getClientOffset()
  }
}

ومع ذلك ، عند تحريك السحب داخل المكون ، لا يتلقى الهدف الدعائم المحدثة مع كل ذبذبة للماوس (فقط عند الدخول والخروج). ومع ذلك ، يتم استدعاء تحوم _is_ المواصفات المستهدفة بشكل متكرر. هل هناك سبب لعدم إدخال الخاصية clientOffset في كل مرة يتم تغييرها؟

bug wontfix

التعليق الأكثر فائدة

+1. الدخول في هذا الأمر وإهدار الكثير من الوقت في محاولة فهم سبب نجاح getClientOffset () في canDrop () - والذي يتم استدعاؤه بشكل متكرر ... - ولكن لا توجد طريقة لإعادة توجيه التحكم إلى "أين" العنصر الذي نحن فيه. أحتاج إلى أن أكون قادرًا على إدراج رمز ثلاثي أعلى وأسفل ، وليس فقط "عليه". ليس لدى canDrop () طريقة يمكنني من خلالها أن أجد تسمح لي في الواقع بإخبار المكون الخاص بي عن "أنواع" الإسقاط العديدة التي تمتلكها العقدة بحيث يمكن إعادة عرض عنصر التحكم الخاص بي وفقًا لذلك. أدى تغيير الخاصية في canDrop إلى حدوث خطأ. لا يعمل الاشتراك في onMouseMove أثناء عمليات السحب والإفلات. لقد كان هذا مضيعة كبيرة للوقت حتى الآن ... أي مساعدة ستكون محل تقدير.

ibash ، هل تمانع في نشر فكرة عما فعلته لحل مشكلتك؟

ال 22 كومينتر

أوه ، نقطة جيدة. هذا صحيح ، لم أتوقع هذا الاستخدام. حاليًا ، الطريقة الوحيدة للاشتراك في تتبع تعويض العميل هي استخدام DragLayer . بخلاف ذلك ، لأسباب تتعلق بالأداء ، فإنه يقوم فقط بتحديث الدعائم إذا تم تغيير شيء يتعلق بالهدف إسقاط نفسه.

أود أن أقول إنها مشكلة في واجهة برمجة التطبيقات ، وقد يكون هناك عدة حلول:

  • نمنع الوصول إلى getClientOffset() وطرق مماثلة من داخل وظيفة collect واطلب استخدام DragLayer بدلاً من ذلك (سهل لكن غبي) ؛
  • اكتشف تلقائيًا أن المستخدم يريد تتبع الإزاحة والاشتراك في تغييرات الإزاحة إذا كانت هذه هي الحالة (أصعب ولكن أكثر اتساقًا).

أعتقد أنني سأقبل العلاقات العامة بالخيار الثاني. يمكنك التحقق من تنفيذ DragLayer لترى كيف يشترك في تغييرات الإزاحة.

لقد بحثت في الخيار الثاني ، لكنه صعب لأن التجميع هو وظيفة ، لذلك ليس من الممكن / من السهل معرفة وظائف الشاشة التي يمكن استدعاءها.

حالة الاستخدام الخاصة بي هي سحب الأدوات التي يتم التقاطها في الموقع ، مما قد يؤدي إلى إزاحة عناصر واجهة المستخدم الأخرى بطريقة معقدة نسبيًا (فكر في سحب أيقونات البرامج على Android). إنه حقًا الهدف الذي يتعين عليه إعادة ترتيب نفسه اعتمادًا على موقع التمرير الدقيق ، في حين أن السحب نفسه لا بأس به تمامًا كلقطة HTML5. يمكنني القيام بذلك باستخدام تحويم المواصفات المستهدفة لتحديث حالة المكون الهدف. لذلك سأترك هذا الآن وأعمل على حله. شكرا لمكتبة رائعة. لدي شيء لائق في العمل في ساعات قليلة جدًا من العمل.

دعونا نبقي هذا مفتوحا الآن. إنه نفس رقم 172: لم أتوقع أن يستخدم الأشخاص دلتا داخل canDrop أو وظيفة التجميع. لن أقوم بإصلاح هذا على الفور ، ولكن قد أتحرر بعد شهر أو نحو ذلك.

أنا أيضا واجهت هذه المشكلة. ما هي أفكارك حول السماح للعملاء بتمرير علامة إلى مواصفات DropTarget للاشتراك في تعويض التغييرات؟ إنه أمر آخر "مسكتك" للمستخدم ، ولكنه أبسط من الاشتراك يدويًا / إلغاء الاشتراك في التعويضات.

ربما لا يمكنك استخدام RxJS-DOM لمراقبة حدث السحب على العنصر وتحديث الحالة بإحداثيات الماوس؟ ربما حل مخترق.

  componentDidMount() {
    this._events.dragOver = DOM.fromEvent(findDOMNode(this.refs.grid), 'dragover')
                            .throttle(200)
                            .subscribe(this.getMouseCoords)
  }

انتهى بي الأمر إلى الاستيلاء على الشاشة والاشتراك لتعويض التغييرات مثل DragLayer. انتهى بي الأمر أيضًا إلى الدخول إلى السجل الداخلي في بعض الأماكن. لا أعتقد أن ما أريد القيام به هو _too_ غير عادي ، لذلك يمكن معالجته IMO من خلال تغييرات API بسيطة.

هل جاء أي شيء من هذا؟ أرغب في مراقبة إزاحة العميل في DropTarget بدلاً من طبقة السحب المخصصة لأنني أشعر أنه من الأسهل تغيير بنية DropTarget عندما يمكنني الوصول إلى موقع التمرير من DropTarget بدلاً من طبقة السحب المخصصة.

+1 أريد أن أفعل بالضبط ما يحاول jchristman فعله.

+1. الدخول في هذا الأمر وإهدار الكثير من الوقت في محاولة فهم سبب نجاح getClientOffset () في canDrop () - والذي يتم استدعاؤه بشكل متكرر ... - ولكن لا توجد طريقة لإعادة توجيه التحكم إلى "أين" العنصر الذي نحن فيه. أحتاج إلى أن أكون قادرًا على إدراج رمز ثلاثي أعلى وأسفل ، وليس فقط "عليه". ليس لدى canDrop () طريقة يمكنني من خلالها أن أجد تسمح لي في الواقع بإخبار المكون الخاص بي عن "أنواع" الإسقاط العديدة التي تمتلكها العقدة بحيث يمكن إعادة عرض عنصر التحكم الخاص بي وفقًا لذلك. أدى تغيير الخاصية في canDrop إلى حدوث خطأ. لا يعمل الاشتراك في onMouseMove أثناء عمليات السحب والإفلات. لقد كان هذا مضيعة كبيرة للوقت حتى الآن ... أي مساعدة ستكون محل تقدير.

ibash ، هل تمانع في نشر فكرة عما فعلته لحل مشكلتك؟

التبديل إلى استخدام hover أخيرًا فعل ذلك بالنسبة لي.

`hover: (props: MyProps، monitor: DropTargetMonitor، component: ReportItemRow) => {
إذا كان (مكون! = فارغ) {
const clientOffset = monitor.getClientOffset () ،

        // Is the dragging node dragged above/below us as opposed to "on" us?

        const elementRect = document.elementFromPoint(clientOffset.x, clientOffset.y).getBoundingClientRect();

        const percentageY = (clientOffset.y - elementRect.top) / elementRect.height;

        // Divide the box up into .25 .5 .25
        const insertDragAndDropMagnetPercent = .25;

        if (insertDragAndDropMagnetPercent >= percentageY) {
            component.setState({ dropTarget: "above" });
        }
        else if (1 - insertDragAndDropMagnetPercent >= percentageY) {
            component.setState({ dropTarget: "inside" });
        }
        else {
            component.setState({ dropTarget: "below" });
        }
    }
},`

@ noah79 ، في غضون انتظار حدوث شيء من هذا ، يمكنك حل مشكلتك عن طريق إنشاء عدد من أهداف الإسقاط ووضعها تحت رموز treenodes الخاصة بك. شيء من هذا القبيل:

         _____
         _____| <-- drop div1 (above treenode1/below top of list)
treenode1_____| <-- drop div2 (on treenode1)
         _____| <-- drop div3 (below treenode1/above treenode2)
treenode2_____| <-- drop div4 (on treenode 2)
         _____| <-- drop div5 (below treenode2/above bottom of list)

ينتهي بك الأمر مع أهداف إسقاط 2n + 1 ، حيث يمثل n عدد العناصر في قائمتك. بعد ذلك ، بناءً على أي من divs يتم تحريك مؤشر الماوس فوقه ، يمكنك تغيير شكل قائمة الشجرة. لقد فعلت شيئًا مشابهًا جدًا لهذا للتغلب على عدم القدرة على الوصول إلى getClientOffset () في الوقت الحالي ، ولم ألاحظ نجاحًا في الأداء. يجب أن يكون ارتفاع كل نقطة div تساوي 1/2 ارتفاع خط treenode1 ويجب أن يكون أول نقطة div عند 1/4 ارتفاع خط treenode1 أعلى من السطر الأول. وهذا يعني أن CSS الخاص بك يجب أن يبدو كما يلي:

.dropdiv-container {
    position: absolute;
    top: -0.25em; /* If the top of this container is aligned with the top of the treenode list initially */
}
.dropdiv {
    height: 0.5em; /* If the height of treenode1 line is 1em with no padding */
}
<div className='dropdiv-container'>
    { renderDropTargets(2 * listLength + 1) }
</div>

هل هذا منطقي؟

إليكم خلاصة كيف حللت هذا. الحيلة هي مجرد الوصول إلى
تفاعل - dnd الداخلي واستخدم الشاشة العالمية مباشرة. ألق نظرة على
مصدر DragDropMonitor.js للحصول على فكرة عن الطرق المتاحة.
المرجع: https://github.com/gaearon/dnd-core/blob/master/src/DragDropMonitor.js

سوف تضطر إلى إعفاء القهوة :)

  # in the component being dragged, get access to the dragDropManager by adding
  # it to the contextTypes
  #
  # dragDropManager is an instance of this:
  # https://github.com/gaearon/dnd-core/blob/master/src/DragDropManager.js

  <strong i="11">@contextTypes</strong>: {
    dragDropManager: React.PropTypes.object
  }

  # because we can receive events after the component is unmounted, we need to
  # keep track of whether the component is mounted manually.
  #
  # <strong i="12">@_monitor</strong> is what lets us access all the nice internal state - it is an instance of this:
  # https://github.com/gaearon/dnd-core/blob/master/src/DragDropMonitor.js

  componentWillMount: () =>
    <strong i="13">@_isMounted</strong> = true
    <strong i="14">@_monitor</strong> = @context.dragDropManager.getMonitor()

    <strong i="15">@_unsubscribeToStateChange</strong> = @_monitor.subscribeToStateChange(@_onStateChange)
    <strong i="16">@_unsubscribeToOffsetChange</strong> = @_monitor.subscribeToOffsetChange(@_onOffsetChange)

  componentWillUnmount: () =>
    <strong i="17">@_isMounted</strong> = false
    <strong i="18">@_monitor</strong> = null

    @_unsubscribeToStateChange()
    @_unsubscribeToOffsetChange()

  # we can access dragging / dropping state by accessing the monitor 

  _onStateChange: () =>
    return unless <strong i="19">@_isMounted</strong>

    # When we stop dragging reset the counter state and hide all cursors.
    if <strong i="20">@_previousIsDragging</strong> && !@_monitor.isDragging()
      console.log('no longer dragging')
    <strong i="21">@_previousIsDragging</strong> = @_monitor.isDragging()

  _onOffsetChange: () =>
    return unless <strong i="22">@_isMounted</strong>

    # mouse is the x/y coordinates
    mouse = @_monitor.getClientOffset()

    # item is the drag item
    item = @_monitor.getItem()

    # if you want to check if a dragged item is over a target, you need the
    # targetId -- in the DropTarget wrapper you can pass it in like:
    #
    #   (connect, monitor) ->
    #     {
    #       targetId: monitor.targetId,
    #       connectDropTarget: connect.dropTarget()
    #     }
    #
    # and then you can use it like below

    @_monitor.isOverTarget(@props.targetId))

هل لهذا معنى؟ إذا لم يكن بإمكاني إضافة المزيد من التفاصيل

كنت أواجه هذا اليوم أيضًا وأهدرت بعض الساعات. ماذا عن ترك ملاحظة حول هذا الموضوع في الوثائق ضمن DropTarget -> وظيفة التجميع؟ من شأن ذلك أن يجنب البعض الآخر على الأقل الإحباط.

بالمناسبة. يمكن أن يكون اختراق آخر قبيح حقًا حول هذه المشكلة هو إرسال الإحداثيات _حالة_ من dropTarget.hover() :

const dropTarget = {
    hover(props, monitor, component) {
        // HACK! Since there is an open bug in react-dnd, making it impossible
        // to get the current client offset through the collect function as the
        // user moves the mouse, we do this awful hack and set the state (!!)
        // on the component from here outside the component.
        component.setState({
            currentDragOffset: monitor.getClientOffset(),
        });
    },
    drop() { /* ... */ },
};

بالطبع هناك الكثير من الطرق الصحيحة للقيام بذلك والتي من شأنها تجنب إساءة استخدام setState من هذا القبيل. ولكن هذا الاختراق الصغير على الأقل مكثف للغاية وقد يؤدي المهمة حتى يتم إصلاح هذه المشكلة في النهاية. يتيح لك الاختراق حول الخطأ دون تغيير المكونات و / أو الملفات الأخرى ودون الاعتماد على المكونات الداخلية للمكتبة.

تحرير: هذا هو في الأساس نفس ما يفعله noah79 ، لم أقرأ الكود الخاص به من قبل الآن.

ألا توجد حقًا طريقة للحصول على موضع الماوس عند الإفلات؟ لا يحتاج المكون المستهدف إلى معرفة ذلك ، لذا فإن setState ليس خيارًا بالنسبة لي. أنا فقط بحاجة إلى الإحداثيات لبدء إجراء إعادة.

من فضلك تجاهل رسالتي الأخيرة. أرى الآن أن drop و endDrag هما جزءان مختلفان من الوظائف ، كما أن وضع المؤشر داخل drop يعمل كما هو متوقع. :)

من الاختبارات التي أجريتها ، يستهلك رد الفعل-dnd أحداث تحريك الماوس بمجرد سحب عنصر فوق الهدف. إذا كان من الممكن أن لا تستهلك html-backend هذا الحدث ، فيمكن للمكون الهدف وضع مستمع mousemove عادي على dom الخاص به بشكل مشروط عندما يتم تعيين الخاصية isOver على true. بعد ذلك ، يمكن لهذا المستمع التقاط موضع الماوس (بالطريقة العادية) عندما يتم سحب شيء ما فوقه. لقد حصلت على هذا للعمل مؤقتًا باستخدام setState من طريقة السحب () يعطي رد الفعل تحذيرات حول تعيين الحالة في منتصف انتقال التقديم.

هذا أوقفني أيضا. +1 للحصول على حل API أو على الأقل شيء في الأسئلة الشائعة.

هل هناك أي خطة لدعم هذا؟
لدي فكرة عن حل بديل خالٍ من الاختراق: قم بعمل غلاف لـ DropTarget والذي يلتف أيضًا hover لتشغيل استدعاء آخر لوظيفة التجميع وإعادة التقديم إذا كانت وظيفة التجميع ترجع قيمًا جديدة .

+1 لهذه المشكلة. حالة الاستخدام لدي هي أن لدي عدة أهداف إسقاط قريبة من بعضها البعض. يبدو من الغريب أنه عند التحليق ، فإن هدف الإسقاط المناسب يبرز ولكن الهبوط في الواقع سينخفض ​​على هدف أقل اعتمادًا على الاتجاه الذي تحوم منه.

لقد عثرت على هذه المشكلة بالبحث عن سبب عدم تحديث عمليات الإزاحة ، ويبدو أنها لا تزال بحاجة إلى حل بديل.
لقد تناولت فكرة @ jedwards1211 وهذا ما توصلت إليه. يحتوي HOC في الغالب على نفس الواجهة مثل vanilla DropTarget ، لكنه يقبل collectRapidly في خياراته وهي قائمة من الدعائم للاستعلام عبر وظيفة التجميع + تمرير كل حدث تحوم. تسبب تمرير جميع الدعائم بشكل عشوائي من وظيفة التجميع في بعض الغرابة ولا يمكننا تمرير connect إلى المجمع على الإطلاق ، لذلك هناك حماية من الاستعلام عن connectDropTarget "بسرعة".

لذا بدلاً من DropTarget(types, target, collect) ستستخدم RapidFireDropTarget(types, target, collect, {collectRapidly: ['offset']}) ، حيث يُفترض أن offset شيء يتلقى قيمته من مجموعة الوظائف monitor.getOffset .

import React from 'react';
import {DropTarget} from 'react-dnd';
import pick from 'lodash/pick';

function RapidFireDropTarget(types, spec, collect, options={}) {
  let collectProps = {};
  const prevHover = spec.hover;
  const {collectRapidly = []} = options;

  const dummyConnect = {
    dropTarget: () => () => {
      throw new Error('Rapidly collected props cannot include results from `connect`');
    }
  };

  const WrappedComponent = Component => {
    return class extends React.Component {
      render() {
        return <Component {...this.props} {...collectProps} />;
      }
    };
  };

  spec.hover = (props, monitor, component) => {
    const allCollectProps = collect(dummyConnect, monitor);

    collectProps = pick(allCollectProps, collectRapidly);
    component.forceUpdate();

    if (prevHover instanceof Function) {
      prevHover(props, monitor, component);
    }
  }

  return (Component) => {
    return DropTarget(types, spec, collect, options)(WrappedComponent(Component));
  };
}

export default RapidFireDropTarget;

تم وضع علامة على هذه المشكلة تلقائيًا على أنها قديمة نظرًا لعدم وجود نشاط حديث لها. سيتم إغلاقه في حالة عدم حدوث أي نشاط آخر. شكرا لمساهماتكم.

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات