ฉันใช้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
เป็นเป้าหมายตกสำหรับIMAGE
s มันสวยมากเหมือนกันยกเว้นว่าเราต้องให้การดำเนินงาน:registerType
dropTarget
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
และสำหรับdragSource
ItemTypes.BLOCK
- สมมติว่าเราเพิ่มบล็อกประเภทอื่น ๆ เราสามารถนำตรรกะการจัดลำดับใหม่มาใช้ใหม่ได้โดยวางไว้ในมิกซ์อิน
dropTargetFor(...types)
อนุญาตให้ระบุหลายประเภทพร้อมกันดังนั้นดร็อปโซนหนึ่งสามารถจับได้หลายประเภท
- เมื่อคุณต้องการการควบคุมแบบละเอียดมากขึ้นเมธอดส่วนใหญ่จะส่งผ่านเหตุการณ์ลากที่ทำให้เกิดเป็นพารามิเตอร์สุดท้าย
สำหรับการขึ้นไปวันที่เอกสารคำแนะนำและการติดตั้งหัวเพื่อตอบสนอง-DND repo บน Github