ฉันใช้react-dndซึ่งเป็นมิกซ์อินแบบลากแล้ววาง HTML5 ที่ยืดหยุ่นสำหรับการตอบสนองด้วยการควบคุม DOM แบบเต็ม
ไลบรารีลากแล้ววางที่มีอยู่ไม่เหมาะกับกรณีการใช้งานของฉันดังนั้นฉันจึงเขียนของตัวเอง คล้ายกับรหัสที่เราใช้งานมาประมาณหนึ่งปีบน Stampsy.com แต่เขียนใหม่เพื่อใช้ประโยชน์จาก React และ Flux
ข้อกำหนดสำคัญที่ฉันมี:
- ปล่อย DOM หรือ CSS ของตัวเองเป็นศูนย์ปล่อยทิ้งไว้ที่ส่วนประกอบที่ใช้
- กำหนดโครงสร้างให้น้อยที่สุดสำหรับส่วนประกอบที่บริโภค
- ใช้การลากและวาง HTML5 เป็นแบ็กเอนด์หลัก แต่ทำให้สามารถเพิ่มแบ็กเอนด์ต่างๆได้ในอนาคต
- เช่นเดียวกับ HTML5 API ดั้งเดิมให้เน้นการลากข้อมูลไม่ใช่แค่ "มุมมองที่ลากได้"
- ซ่อน HTML5 API quirks จากโค้ดที่ใช้;
- ส่วนประกอบที่แตกต่างกันอาจเป็น "แหล่งที่มาของการลาก" หรือ "เป้าหมายที่วาง" สำหรับข้อมูลประเภทต่างๆ
- อนุญาตให้ส่วนประกอบหนึ่งมีแหล่งข้อมูลการลากหลายแหล่งและวางเป้าหมายเมื่อจำเป็น
- ทำให้เป้าหมายการดร็อปเปลี่ยนรูปลักษณ์ได้ง่ายหากมีการลากหรือวางข้อมูลที่เข้ากันได้
- ทำให้ง่ายต่อการใช้ภาพเพื่อลากภาพขนาดย่อแทนภาพหน้าจอขององค์ประกอบหลีกเลี่ยงความแปลกประหลาดของเบราว์เซอร์
หากเสียงเหล่านี้คุ้นเคยสำหรับคุณอ่านต่อ
การใช้งาน
แหล่งลากอย่างง่าย
ขั้นแรกประกาศประเภทข้อมูลที่สามารถลากได้
สิ่งเหล่านี้ใช้เพื่อตรวจสอบ "ความเข้ากันได้" ของแหล่งที่มาของการลากและวางเป้าหมาย:
module.exports = {
BLOCK: 'block',
IMAGE: 'image'
};
(หากคุณไม่มีข้อมูลหลายประเภท Libary นี้อาจไม่เหมาะกับคุณ)
จากนั้นมาสร้างส่วนประกอบที่ลากได้ง่ายมากซึ่งเมื่อลากจะแสดงถึงIMAGE:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var Image = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
dragSource: {
beginDrag() {
return {
item: this.props.image
};
}
}
});
},
render() {
return (
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
);
}
);
โดยการระบุconfigureDragDropเราบอกDragDropMixinพฤติกรรมการลากวางของส่วนประกอบนี้ ทั้งส่วนประกอบที่ลากได้และแบบหยดได้ใช้ส่วนผสมเดียวกัน
ข้างในconfigureDragDropเราจำเป็นต้องเรียกร้องregisterTypeให้แต่ละItemTypesส่วนที่กำหนดเองของเรารองรับ ตัวอย่างเช่นอาจมีการแสดงภาพหลายภาพในแอปของคุณและแต่ละภาพจะระบุdragSourceสำหรับItemTypes.IMAGEสำหรับ
A dragSourceเป็นเพียงออบเจ็กต์ที่ระบุว่าแหล่งข้อมูลการลากทำงานอย่างไร คุณต้องใช้beginDragเพื่อส่งคืนรายการที่แสดงถึงข้อมูลที่คุณกำลังลากและตัวเลือกบางตัวเลือกที่จะปรับ UI การลาก คุณสามารถเลือกที่canDragจะใช้เพื่อห้ามการลากหรือendDrag(didDrop)เรียกใช้ตรรกะบางอย่างเมื่อเกิดการดร็อป (หรือไม่มี) และคุณสามารถแชร์ตรรกะนี้ระหว่างคอมโพเนนต์โดยปล่อยให้มิกซ์อินที่แชร์สร้างdragSourceให้
สุดท้ายคุณต้องใช้{...this.dragSourceFor(itemType)}กับองค์ประกอบบางอย่าง (หนึ่งหรือมากกว่า) renderเพื่อแนบตัวจัดการการลาก ซึ่งหมายความว่าคุณสามารถมี "ที่จับสำหรับลาก" ได้หลายแบบในองค์ประกอบเดียวและอาจเกี่ยวข้องกับรายการประเภทต่างๆ (หากคุณไม่คุ้นเคยกับJSX Spread Attributesไวยากรณ์โปรดตรวจสอบ)
วางเป้าหมายง่ายๆ
สมมติว่าเราต้องการImageBlockเป็นเป้าหมายตกสำหรับIMAGEs มันสวยมากเหมือนกันยกเว้นว่าเราต้องให้การดำเนินงาน:registerTypedropTarget
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
dropTarget: {
acceptDrop(image) {
DocumentActionCreators.setImage(this.props.blockId, image);
}
}
});
},
render() {
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{this.props.image &&
<img src={this.props.image.url} />
}
</div>
);
}
);
ลาก Source + Drop Target ในส่วนประกอบเดียว
สมมติว่าตอนนี้เราต้องการให้ผู้ใช้สามารถลากรูปภาพออกมาImageBlockได้ เราต้องเพิ่มความเหมาะสมdragSourceและตัวจัดการสองสามตัว:
var { DragDropMixin } = require('react-dnd'),
ItemTypes = require('./ItemTypes');
var ImageBlock = React.createClass({
mixins: [DragDropMixin],
configureDragDrop(registerType) {
registerType(ItemTypes.IMAGE, {
dragSource: {
canDrag() {
return !!this.props.image;
},
beginDrag() {
return {
item: this.props.image
};
}
}
dropTarget: {
acceptDrop(image) {
DocumentActionCreators.setImage(this.props.blockId, image);
}
}
});
},
render() {
return (
<div {...this.dropTargetFor(ItemTypes.IMAGE)}>
{/* Add {...this.dragSourceFor} handlers to a nested node */}
{this.props.image &&
<img src={this.props.image.url}
{...this.dragSourceFor(ItemTypes.IMAGE)} />
}
</div>
);
}
);
อะไรที่เป็นไปได้?
ฉันไม่ได้ครอบคลุมทุกอย่าง แต่เป็นไปได้ที่จะใช้ API นี้ได้หลายวิธี:
- ใช้
getDragState(type)และgetDropState(type)เรียนรู้ว่าการลากทำงานอยู่หรือไม่และใช้เพื่อสลับคลาส CSS หรือแอตทริบิวต์
- ระบุ
dragPreviewว่าImageจะใช้รูปภาพเป็นตัวยึดตำแหน่งลาก (ใช้ImagePreloaderMixinเพื่อโหลด);
- พูดว่าเราต้องการที่จะ
ImageBlocksปรับเปลี่ยนได้ เราจะต้องให้พวกเขาในการดำเนินการdropTargetและสำหรับdragSourceItemTypes.BLOCK
- สมมติว่าเราเพิ่มบล็อกประเภทอื่น ๆ เราสามารถนำตรรกะการจัดลำดับใหม่มาใช้ใหม่ได้โดยวางไว้ในมิกซ์อิน
dropTargetFor(...types) อนุญาตให้ระบุหลายประเภทพร้อมกันดังนั้นดร็อปโซนหนึ่งสามารถจับได้หลายประเภท
- เมื่อคุณต้องการการควบคุมแบบละเอียดมากขึ้นเมธอดส่วนใหญ่จะส่งผ่านเหตุการณ์ลากที่ทำให้เกิดเป็นพารามิเตอร์สุดท้าย
สำหรับการขึ้นไปวันที่เอกสารคำแนะนำและการติดตั้งหัวเพื่อตอบสนอง-DND repo บน Github